Repository: galdolber/clojure-objc
Branch: master
Commit: a56dca4ea382
Files: 349
Total size: 3.3 MB
Directory structure:
gitextract_r576sy1c/
├── .gitignore
├── CONTRIBUTING.md
├── KNOWN_ISSUES
├── antsetup.sh
├── build.clj
├── build.xml
├── changes.md
├── clojure.iml
├── doc/
│ └── clojure/
│ └── pprint/
│ ├── CommonLispFormat.markdown
│ └── PrettyPrinting.markdown
├── epl-v10.html
├── pom.xml
├── readme.md
├── release.sh
├── src/
│ ├── assembly/
│ │ ├── distribution.xml
│ │ └── slim.xml
│ ├── clj/
│ │ └── clojure/
│ │ ├── core/
│ │ │ ├── protocols.clj
│ │ │ └── reducers.clj
│ │ ├── core.clj
│ │ ├── core_deftype.clj
│ │ ├── core_objc.clj
│ │ ├── core_print.clj
│ │ ├── core_proxy.clj
│ │ ├── data.clj
│ │ ├── edn.clj
│ │ ├── genclass.clj
│ │ ├── gvec.clj
│ │ ├── inspector.clj
│ │ ├── instant.clj
│ │ ├── java/
│ │ │ ├── browse.clj
│ │ │ ├── browse_ui.clj
│ │ │ ├── io.clj
│ │ │ ├── javadoc.clj
│ │ │ └── shell.clj
│ │ ├── main.clj
│ │ ├── parallel.clj
│ │ ├── pprint/
│ │ │ ├── cl_format.clj
│ │ │ ├── column_writer.clj
│ │ │ ├── dispatch.clj
│ │ │ ├── pprint_base.clj
│ │ │ ├── pretty_writer.clj
│ │ │ ├── print_table.clj
│ │ │ └── utilities.clj
│ │ ├── pprint.clj
│ │ ├── reflect/
│ │ │ └── java.clj
│ │ ├── reflect.clj
│ │ ├── remoterepl.clj
│ │ ├── repl.clj
│ │ ├── set.clj
│ │ ├── stacktrace.clj
│ │ ├── string.clj
│ │ ├── template.clj
│ │ ├── test/
│ │ │ ├── junit.clj
│ │ │ └── tap.clj
│ │ ├── test.clj
│ │ ├── uuid.clj
│ │ ├── walk.clj
│ │ ├── xml.clj
│ │ └── zip.clj
│ ├── ffi/
│ │ ├── ffi.h
│ │ ├── ffi_arm64.h
│ │ ├── ffi_armv7.h
│ │ ├── ffi_common.h
│ │ ├── ffi_i386.h
│ │ ├── ffi_x86_64.h
│ │ ├── fficonfig.h
│ │ ├── fficonfig_arm64.h
│ │ ├── fficonfig_armv7.h
│ │ ├── fficonfig_i386.h
│ │ ├── fficonfig_x86_64.h
│ │ ├── ffitarget.h
│ │ ├── ffitarget_arm64.h
│ │ ├── ffitarget_armv7.h
│ │ ├── ffitarget_i386.h
│ │ ├── ffitarget_x86_64.h
│ │ └── libffi.a
│ ├── jvm/
│ │ ├── clojure/
│ │ │ ├── asm/
│ │ │ │ ├── AnnotationVisitor.java
│ │ │ │ ├── AnnotationWriter.java
│ │ │ │ ├── Attribute.java
│ │ │ │ ├── ByteVector.java
│ │ │ │ ├── ClassReader.java
│ │ │ │ ├── ClassVisitor.java
│ │ │ │ ├── ClassWriter.java
│ │ │ │ ├── Context.java
│ │ │ │ ├── Edge.java
│ │ │ │ ├── FieldVisitor.java
│ │ │ │ ├── FieldWriter.java
│ │ │ │ ├── Frame.java
│ │ │ │ ├── Handle.java
│ │ │ │ ├── Handler.java
│ │ │ │ ├── Item.java
│ │ │ │ ├── Label.java
│ │ │ │ ├── MethodVisitor.java
│ │ │ │ ├── MethodWriter.java
│ │ │ │ ├── Opcodes.java
│ │ │ │ ├── Type.java
│ │ │ │ ├── commons/
│ │ │ │ │ ├── AdviceAdapter.java
│ │ │ │ │ ├── AnalyzerAdapter.java
│ │ │ │ │ ├── CodeSizeEvaluator.java
│ │ │ │ │ ├── GeneratorAdapter.java
│ │ │ │ │ ├── InstructionAdapter.java
│ │ │ │ │ ├── LocalVariablesSorter.java
│ │ │ │ │ ├── Method.java
│ │ │ │ │ ├── SerialVersionUIDAdder.java
│ │ │ │ │ ├── StaticInitMerger.java
│ │ │ │ │ ├── TableSwitchGenerator.java
│ │ │ │ │ └── package.html
│ │ │ │ └── package.html
│ │ │ ├── java/
│ │ │ │ └── api/
│ │ │ │ ├── Clojure.java
│ │ │ │ └── package.html
│ │ │ ├── lang/
│ │ │ │ ├── AFn.java
│ │ │ │ ├── AFunction.java
│ │ │ │ ├── AMapEntry.java
│ │ │ │ ├── APersistentMap.java
│ │ │ │ ├── APersistentSet.java
│ │ │ │ ├── APersistentVector.java
│ │ │ │ ├── ARef.java
│ │ │ │ ├── AReference.java
│ │ │ │ ├── ASeq.java
│ │ │ │ ├── ATransientMap.java
│ │ │ │ ├── ATransientSet.java
│ │ │ │ ├── Agent.java
│ │ │ │ ├── ArityException.java
│ │ │ │ ├── ArrayChunk.java
│ │ │ │ ├── ArrayIter.java
│ │ │ │ ├── ArraySeq.java
│ │ │ │ ├── Associative.java
│ │ │ │ ├── Atom.java
│ │ │ │ ├── BigInt.java
│ │ │ │ ├── Binding.java
│ │ │ │ ├── Box.java
│ │ │ │ ├── ChunkBuffer.java
│ │ │ │ ├── ChunkedCons.java
│ │ │ │ ├── Compile.java
│ │ │ │ ├── Compiler.java
│ │ │ │ ├── Cons.java
│ │ │ │ ├── Counted.java
│ │ │ │ ├── Cycle.java
│ │ │ │ ├── Delay.java
│ │ │ │ ├── DynamicClassLoader.java
│ │ │ │ ├── EdnReader.java
│ │ │ │ ├── EnumerationSeq.java
│ │ │ │ ├── ExceptionInfo.java
│ │ │ │ ├── Fn.java
│ │ │ │ ├── FnLoaderThunk.java
│ │ │ │ ├── IAtom.java
│ │ │ │ ├── IBlockingDeref.java
│ │ │ │ ├── IChunk.java
│ │ │ │ ├── IChunkedSeq.java
│ │ │ │ ├── IDeref.java
│ │ │ │ ├── IEditableCollection.java
│ │ │ │ ├── IExceptionInfo.java
│ │ │ │ ├── IFn.java
│ │ │ │ ├── IHashEq.java
│ │ │ │ ├── IKeywordLookup.java
│ │ │ │ ├── ILookup.java
│ │ │ │ ├── ILookupSite.java
│ │ │ │ ├── ILookupThunk.java
│ │ │ │ ├── IMapEntry.java
│ │ │ │ ├── IMapIterable.java
│ │ │ │ ├── IMeta.java
│ │ │ │ ├── IObj.java
│ │ │ │ ├── IPending.java
│ │ │ │ ├── IPersistentCollection.java
│ │ │ │ ├── IPersistentList.java
│ │ │ │ ├── IPersistentMap.java
│ │ │ │ ├── IPersistentSet.java
│ │ │ │ ├── IPersistentStack.java
│ │ │ │ ├── IPersistentVector.java
│ │ │ │ ├── IProxy.java
│ │ │ │ ├── IRecord.java
│ │ │ │ ├── IReduce.java
│ │ │ │ ├── IReduceInit.java
│ │ │ │ ├── IRef.java
│ │ │ │ ├── IReference.java
│ │ │ │ ├── ISeq.java
│ │ │ │ ├── ITransientAssociative.java
│ │ │ │ ├── ITransientCollection.java
│ │ │ │ ├── ITransientMap.java
│ │ │ │ ├── ITransientSet.java
│ │ │ │ ├── ITransientVector.java
│ │ │ │ ├── IType.java
│ │ │ │ ├── IllegalAccessError.java
│ │ │ │ ├── Indexed.java
│ │ │ │ ├── IndexedSeq.java
│ │ │ │ ├── Intrinsics.java
│ │ │ │ ├── Iterate.java
│ │ │ │ ├── IteratorSeq.java
│ │ │ │ ├── Keyword.java
│ │ │ │ ├── KeywordLookupSite.java
│ │ │ │ ├── LazilyPersistentVector.java
│ │ │ │ ├── LazySeq.java
│ │ │ │ ├── LineNumberingPushbackReader.java
│ │ │ │ ├── LispReader.java
│ │ │ │ ├── LockingTransaction.java
│ │ │ │ ├── LongRange.java
│ │ │ │ ├── MapEntry.java
│ │ │ │ ├── MapEquivalence.java
│ │ │ │ ├── MethodImplCache.java
│ │ │ │ ├── MultiFn.java
│ │ │ │ ├── Murmur3.java
│ │ │ │ ├── Named.java
│ │ │ │ ├── Namespace.java
│ │ │ │ ├── Numbers.java
│ │ │ │ ├── Obj.java
│ │ │ │ ├── ObjC.java
│ │ │ │ ├── ObjCClass.java
│ │ │ │ ├── PersistentArrayMap.java
│ │ │ │ ├── PersistentHashMap.java
│ │ │ │ ├── PersistentHashSet.java
│ │ │ │ ├── PersistentList.java
│ │ │ │ ├── PersistentQueue.java
│ │ │ │ ├── PersistentStructMap.java
│ │ │ │ ├── PersistentTreeMap.java
│ │ │ │ ├── PersistentTreeSet.java
│ │ │ │ ├── PersistentVector.java
│ │ │ │ ├── ProxyHandler.java
│ │ │ │ ├── RT.java
│ │ │ │ ├── Range.java
│ │ │ │ ├── Ratio.java
│ │ │ │ ├── ReaderConditional.java
│ │ │ │ ├── RecordIterator.java
│ │ │ │ ├── Reduced.java
│ │ │ │ ├── Ref.java
│ │ │ │ ├── Reflector.java
│ │ │ │ ├── RemoteRef.java
│ │ │ │ ├── RemoteRepl.java
│ │ │ │ ├── Repeat.java
│ │ │ │ ├── RestFn.java
│ │ │ │ ├── RestFnWithMeta.java
│ │ │ │ ├── Reversible.java
│ │ │ │ ├── Selector.java
│ │ │ │ ├── SeqEnumeration.java
│ │ │ │ ├── SeqIterator.java
│ │ │ │ ├── Seqable.java
│ │ │ │ ├── Sequential.java
│ │ │ │ ├── Settable.java
│ │ │ │ ├── Sorted.java
│ │ │ │ ├── SourceGenIntrinsics.java
│ │ │ │ ├── SourceWriter.java
│ │ │ │ ├── StringEscapeUtils.java
│ │ │ │ ├── StringSeq.java
│ │ │ │ ├── Symbol.java
│ │ │ │ ├── TaggedLiteral.java
│ │ │ │ ├── ThreadFactory.java
│ │ │ │ ├── TransactionalHashMap.java
│ │ │ │ ├── TransformerIterator.java
│ │ │ │ ├── URLClassLoader.java
│ │ │ │ ├── Util.java
│ │ │ │ ├── Var.java
│ │ │ │ ├── Volatile.java
│ │ │ │ ├── WarnBoxedMath.java
│ │ │ │ ├── XMLHandler.java
│ │ │ │ └── package.html
│ │ │ └── main.java
│ │ └── com/
│ │ └── google/
│ │ └── j2objc/
│ │ └── annotations/
│ │ └── ReflectionSupport.java
│ ├── objc/
│ │ ├── Cst502Socket.h
│ │ ├── Cst502Socket.m
│ │ ├── NSCommon.h
│ │ ├── NSCommon.m
│ │ ├── NSProxyImpl.h
│ │ ├── NSProxyImpl.m
│ │ ├── NSSocketImpl.h
│ │ ├── NSSocketImpl.m
│ │ ├── NSTypeImpl.h
│ │ ├── NSTypeImpl.m
│ │ ├── ReplClient.h
│ │ ├── ReplClient.m
│ │ ├── WeakRef.h
│ │ └── WeakRef.m
│ ├── resources/
│ │ └── clojure/
│ │ └── version.properties
│ └── script/
│ ├── run_test.clj
│ └── run_test_generative.clj
└── test/
├── clojure/
│ ├── test_clojure/
│ │ ├── agents.clj
│ │ ├── annotations/
│ │ │ ├── java_5.clj
│ │ │ └── java_6.clj
│ │ ├── annotations.clj
│ │ ├── api.clj
│ │ ├── atoms.clj
│ │ ├── clojure_set.clj
│ │ ├── clojure_walk.clj
│ │ ├── clojure_xml.clj
│ │ ├── clojure_zip.clj
│ │ ├── compilation/
│ │ │ ├── examples.clj
│ │ │ └── line_number_examples.clj
│ │ ├── compilation.clj
│ │ ├── control.clj
│ │ ├── data.clj
│ │ ├── data_structures.clj
│ │ ├── def.clj
│ │ ├── delays.clj
│ │ ├── edn.clj
│ │ ├── errors.clj
│ │ ├── evaluation.clj
│ │ ├── fn.clj
│ │ ├── for.clj
│ │ ├── genclass/
│ │ │ └── examples.clj
│ │ ├── genclass.clj
│ │ ├── generators.clj
│ │ ├── java/
│ │ │ ├── io.clj
│ │ │ ├── javadoc.clj
│ │ │ └── shell.clj
│ │ ├── java_interop.clj
│ │ ├── keywords.clj
│ │ ├── logic.clj
│ │ ├── macros.clj
│ │ ├── main.clj
│ │ ├── metadata.clj
│ │ ├── multimethods.clj
│ │ ├── ns_libs.clj
│ │ ├── numbers.clj
│ │ ├── other_functions.clj
│ │ ├── parallel.clj
│ │ ├── pprint/
│ │ │ ├── test_cl_format.clj
│ │ │ ├── test_helper.clj
│ │ │ └── test_pretty.clj
│ │ ├── pprint.clj
│ │ ├── predicates.clj
│ │ ├── printer.clj
│ │ ├── protocols/
│ │ │ ├── examples.clj
│ │ │ ├── hash_collisions.clj
│ │ │ └── more_examples.clj
│ │ ├── protocols.clj
│ │ ├── reader.cljc
│ │ ├── reducers.clj
│ │ ├── reflect.clj
│ │ ├── refs.clj
│ │ ├── repl/
│ │ │ └── example.clj
│ │ ├── repl.clj
│ │ ├── rt.clj
│ │ ├── sequences.clj
│ │ ├── serialization.clj
│ │ ├── special.clj
│ │ ├── string.clj
│ │ ├── test.clj
│ │ ├── test_fixtures.clj
│ │ ├── transducers.clj
│ │ ├── transients.clj
│ │ ├── try_catch.clj
│ │ ├── vars.clj
│ │ ├── vectors.clj
│ │ └── volatiles.clj
│ └── test_helper.clj
├── java/
│ ├── clojure/
│ │ └── test/
│ │ └── ReflectorTryCatchFixture.java
│ ├── compilation/
│ │ └── TestDispatch.java
│ └── java/
│ └── util/
│ └── jar/
│ ├── JarEntry.java
│ └── JarFile.java
└── objc/
├── NSCommonTest.h
└── NSCommonTest.m
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.jar
target
clojure.iws
clojure.ipr
nbproject/private/
maven-classpath
maven-classpath.properties
coclojure/*
.settings/*
.classpath
.project
xcode
.idea/
settings.xml
upload-github.sh
================================================
FILE: CONTRIBUTING.md
================================================
If you'd like to submit a patch, please follow the [contributing guidelines](http://clojure.org/contributing).
================================================
FILE: KNOWN_ISSUES
================================================
- Dividing by zero doesn't throw an exception in objc, it fails with EXC_ARITHMETIC
- NullPointerException and ClassCastException are not reliable, as they are emulated by j2objc
- Empty regular expressions throw an exception
- pr-str a date have a bad Timezone format: https://code.google.com/p/j2objc/issues/detail?id=321
================================================
FILE: antsetup.sh
================================================
#!/bin/bash
mvn -q dependency:build-classpath -Dmdep.outputFile=maven-classpath
cat <maven-classpath.properties
maven.compile.classpath=`cat maven-classpath`
maven.test.classpath=`cat maven-classpath`
EOF
echo "Wrote maven-classpath.properties for standalone ant use"
================================================
FILE: build.clj
================================================
(use '[clojure.java.shell :only [sh with-sh-dir]])
(use '[clojure.java.io :only [delete-file file]])
(require '[clojure.string :as st])
(import '[java.io File])
(def j2objc-home (System/getenv "J2OBJC_HOME"))
(def iphone-os-sdk "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk")
(def iphone-simulator-sdk "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk")
(def frameworks "-framework UIKit -framework Foundation")
(def opts "-g -miphoneos-version-min=6.1 -fmessage-length=0 -fmacro-backtrace-limit=0 -std=gnu99 -fpascal-strings -O3 -DDEBUG=1 -Wno-unsequenced")
(let [a (agent nil)]
(defn println+ [& other]
(send a (fn [_] (apply println other)))))
(defn walk [^File dir]
(let [children (.listFiles dir)
subdirs (filter #(.isDirectory %) children)
files (filter #(.isFile %) children)]
(concat files (mapcat walk subdirs))))
(defn find-files [folder extension]
(filter #(.endsWith (.getName %) (str "." extension)) (walk (file folder))))
(defn sh+ [& args]
(let [silent (= :silent (first args))
_ (when-not silent
(println+ "Running: " (reduce str (interpose " " args))))
args (map #(clojure.string/split % #" ")
(if silent (next args) args))
r (apply sh (flatten args))]
(when-not (zero? (:exit r))
(println+ "FAILED" (:err r)))))
(defn makeoname [f]
(str (st/replace f #"/" ".") ".o"))
(defn clang [id params sdk target f]
(println+ id (.getName f))
(sh+ :silent "clang" "-x" "objective-c" params opts "-isysroot" sdk
(str "-I" target "/../src/ffi")
(str "-I" target "/objc")
(str "-I" j2objc-home "/include")
"-c" (.getCanonicalPath f) "-o" (makeoname (.getPath f))))
(defn build [id params sdk]
(let [target (File. "target")
tcn (.getCanonicalPath target)]
(with-sh-dir target
(println+ "Compiling" id)
(sh+ "rm" "-Rf" id)
(sh+ "mkdir" id)
(with-sh-dir (File. (str "target/" id))
(doall (pmap (partial clang id params sdk tcn)
(find-files "target/objc" "m")))
(spit (str tcn "/" id "/files.LinkFileList") (reduce str (interpose "\n" (find-files (str tcn "/" id) "o"))))
(sh+ "libtool" "-static" "-syslibroot" sdk "-filelist"
"files.LinkFileList" frameworks "-o" "libclojure-objc.a")))))
(sh+ "mvn" "compile" "test-compile")
(sh+ "rm" "-Rf" "target/objc")
(sh+ "mkdir" "target/objc")
(sh+ "cp" "-R" "src/objc/." "target/objc")
(sh+ "cp" "-R" "src/ffi/." "target/objc")
(sh+ "zip" "-r" "target/objc.jar" "target/gen" "src/jvm" "test/java")
(sh+ (str j2objc-home "/j2objc") "-d" "target/objc" "--final-methods-as-functions" "--batch-translate-max=500" "-J-Xmx2G" "-classpath"
"target/classes:target/test-classes"
"target/objc.jar")
(let [i (File. "target/include")]
(when-not (.exists i)
(.mkdirs i)))
(with-sh-dir (File. "target/objc")
(sh "rsync" "-avm" "--delete" "--include" "*.h" "-f"
"hide,! */" "." "../include"))
(build "iphoneos" "-arch armv7 -arch armv7s -arch arm64" iphone-os-sdk)
(build "iphonesimulator" "-arch i386 -arch x86_64" iphone-simulator-sdk)
(let [a (File. "target/libclojure-objc.a")]
(when (.exists a)
(.delete a))
(sh+ "lipo" "-create" "-output" "target/libclojure-objc.a" "target/iphoneos/libclojure-objc.a" "target/iphonesimulator/libclojure-objc.a"))
================================================
FILE: build.xml
================================================
Build with "ant" and then start the
REPL with: "java -cp clojure.jar clojure.main".
version=${clojure.version.label}
================================================
FILE: changes.md
================================================
# Changes to Clojure in Version 1.7
## 1 Compatibility Notes
Please be aware of the following issues when upgrading to Clojure 1.7.
### Seqs on Java iterators that return the same mutating object
Seqs are fundamentally incompatible with Java iterators that return
the same mutating object on every call to next(). Some Clojure
libraries incorrectly rely on calling seq on such iterators.
In 1.7, iterator-seqs are chunked, which will cause many of these
incorrect usages to return incorrect results immediately.
The `seq` and `iterator-seq` docstrings have been updated to include
an explicit warning. Libraries that incorrectly use `seq` and
`iterator-seq` will need to be fixed before running against 1.7.
* [CLJ-1669](http://dev.clojure.org/jira/browse/CLJ-1669)
* [CLJ-1738](http://dev.clojure.org/jira/browse/CLJ-1738)
### Thread owner check removed on transients
Prior to Clojure 1.7, transients would allow modification only from the
thread that created the transient. This check has been removed. It is
still a requirement that transients should be updated by only a single
thread at a time.
This constraint was relaxed to allow transients to be used in cases where
code is multiplexed across multiple threads in a pool (such as go blocks
in core.async).
### keys/vals require custom map type to implement Iterable
Invoking `keys` or `vals` on a custom map type that implements IPersistentMap
will now use the Iterable iterator() method instead of accessing entries
via the seq of the map. There have been no changes in the type hierarchy
(IPersistentMap has always extended Iterable) but former map-like instances
may have skipped implementing this method in the past.
* [CLJ-1602](http://dev.clojure.org/jira/browse/CLJ-1602)
## 2 New and Improved Features
### 2.1 Transducers
Transducers is a new way to decouple algorithmic transformations from their
application in different contexts. Transducers are functions that transform
reducing functions to build up a "recipe" for transformation.
Also see: http://clojure.org/transducers
Many existing sequence functions now have a new arity (one fewer argument
than before). This arity will return a transducer that represents the same
logic but is independent of lazy sequence processing. Functions included are:
* map
* mapcat
* filter
* remove
* take
* take-while
* drop
* drop-while
* take-nth
* replace
* partition-by
* partition-all
* keep
* keep-indexed
* map-indexed
* distinct
* interpose
Additionally some new transducer functions have been added:
* cat - concatenates the contents of each input
* dedupe - removes consecutive duplicated values
* random-sample - returns items from coll with random probability
And this function can be used to make completing transforms:
* completing
There are also several new or modified functions that can be used to apply
transducers in different ways:
* sequence - takes a transformation and a coll and produces a lazy seq
* transduce - reduce with a transformation (eager)
* eduction - returns a reducible/iterable of applications of the transducer to items in coll. Applications are re-performed with every reduce/iterator.
There have been a number of internal changes to support transducers:
* volatiles - there are a new set of functions (volatile!, vswap!, vreset!, volatile?) to create and use volatile "boxes" to hold state in stateful transducers. Volatiles are faster than atoms but give up atomicity guarantees so should only be used with thread isolation.
* array iterators - added support for iterators over arrays
* conj can be used as a reducing function and will conj to []
Some related issues addressed during development:
* [CLJ-1511](http://dev.clojure.org/jira/browse/CLJ-1511)
* [CLJ-1497](http://dev.clojure.org/jira/browse/CLJ-1497)
* [CLJ-1549](http://dev.clojure.org/jira/browse/CLJ-1549)
* [CLJ-1537](http://dev.clojure.org/jira/browse/CLJ-1537)
* [CLJ-1554](http://dev.clojure.org/jira/browse/CLJ-1554)
* [CLJ-1601](http://dev.clojure.org/jira/browse/CLJ-1601)
* [CLJ-1606](http://dev.clojure.org/jira/browse/CLJ-1606)
* [CLJ-1621](http://dev.clojure.org/jira/browse/CLJ-1621)
* [CLJ-1600](http://dev.clojure.org/jira/browse/CLJ-1600)
* [CLJ-1635](http://dev.clojure.org/jira/browse/CLJ-1635)
* [CLJ-1683](http://dev.clojure.org/jira/browse/CLJ-1683)
* [CLJ-1669](http://dev.clojure.org/jira/browse/CLJ-1669)
* [CLJ-1723](http://dev.clojure.org/jira/browse/CLJ-1723)
### 2.2 Reader Conditionals
Reader Conditionals are a new capability to support portable code that
can run on multiple Clojure platforms with only small changes. In
particular, this feature aims to support the increasingly common case
of libraries targeting both Clojure and ClojureScript.
Code intended to be common across multiple platforms should use a new
supported file extension: ".cljc". When requested to load a namespace,
the platform-specific file extension (.clj, .cljs) will be checked
prior to .cljc.
A new reader form can be used to specify "reader conditional" code in
cljc files (and *only* cljc files). Each platform defines a feature
identifying the platform (:clj, :cljs, :cljr). The reader conditional
specifies code that is read conditionally based on the feature. The
REPL also allows reader conditionals.
Form #? takes a list of alternating feature and expression. These are
checked like cond and the selected expression is read and returned. Other
branches are read but skipped. If no branch is selected, the reader reads
nothing (not nil, but literally as if reading no form). An optional
`:default` branch can be used as a fallthrough.
Reader conditional with 2 features and a default:
#?(:clj Double/NaN
:cljs js/NaN
:default nil)
There is also a reader conditional splicing form. The evaluated expression
should be sequential and will be spliced into the surrounded code, similar
to unquote-splicing.
For example:
[1 2 #?@(:clj [3 4] :cljs [5 6])]
This form would read as [1 2 3 4] on Clojure, [1 2 5 6] on ClojureScript,
and [1 2] on any other platform. Splicing is not allowed at the top level.
Additionally, the reader can now be invoked with options for the features
to use and how to interpret reader conditionals. By default, reader conditionals
are not allowed, but that can be turned on, or a "preserve" mode can be used to
preserve all branches (most likely useful for tooling or source transforms).
In the preserve mode, the reader conditional itself and any tagged literals
within the unselected branches are returned as tagged literal data.
For more information, see:
http://dev.clojure.org/display/design/Reader+Conditionals
* [CLJ-1424](http://dev.clojure.org/jira/browse/CLJ-1424)
* [CLJ-1685](http://dev.clojure.org/jira/browse/CLJ-1685)
* [CLJ-1698](http://dev.clojure.org/jira/browse/CLJ-1698)
* [CLJ-1699](http://dev.clojure.org/jira/browse/CLJ-1699)
* [CLJ-1700](http://dev.clojure.org/jira/browse/CLJ-1700)
* [CLJ-1728](http://dev.clojure.org/jira/browse/CLJ-1728)
* [CLJ-1706](http://dev.clojure.org/jira/browse/CLJ-1706)
### 2.3 Keyword and Symbol Construction
In response to issues raised in [CLJ-1439](http://dev.clojure.org/jira/browse/CLJ-1439),
several changes have been made in symbol and keyword construction:
1) The main bottleneck in construction of symbols (which also occurs inside keywords) was
interning of the name and namespace strings. This interning has been removed, resulting
in a performance increase.
2) Keywords are cached and keyword construction includes a cache check. A change was made
to only clear the cache reference queue when there is a cache miss.
### 2.4 Warn on Boxed Math
One source of performance issues is the (unintended) use of arithmetic operations on
boxed numbers. To make detecting the presence of boxed math easier, a warning will now
be emitted about boxed math if \*unchecked-math* is set to :warn-on-boxed (any truthy
value will enable unchecked-math, only this specific value enables the warning).
Example use:
user> (defn plus-2 [x] (+ x 2)) ;; no warning, but boxed
#'user/plus-2
user> (set! *unchecked-math* :warn-on-boxed)
true
user> (defn plus-2 [x] (+ x 2)) ;; now we see a warning
Boxed math warning, NO_SOURCE_PATH:10:18 - call: public static java.lang.Number
clojure.lang.Numbers.unchecked_add(java.lang.Object,long).
#'user/plus-2
user> (defn plus-2 [^long x] (+ x 2)) ;; use a hint to avoid boxing
#'user/plus-2
* [CLJ-1325](http://dev.clojure.org/jira/browse/CLJ-1325)
* [CLJ-1535](http://dev.clojure.org/jira/browse/CLJ-1535)
* [CLJ-1642](http://dev.clojure.org/jira/browse/CLJ-1642)
### 2.5 update - like update-in for first level
`update` is a new function that is like update-in specifically for first-level keys:
(update m k f args...)
Example use:
user> (update {:a 1} :a inc)
{:a 2}
user> (update {:a 1} :a + 2)
{:a 3}
user> (update {} :a identity) ;; missing returns nil
{:a nil}
* [CLJ-1251](http://dev.clojure.org/jira/browse/CLJ-1251)
### 2.6 Faster reduce and iterator paths
Several important Clojure functions now return sequences that also
contain fast reduce() (or in some cases iterator()) paths. In many
cases, the new implementations are also faster for lazy sequences
* repeat - now implements IReduce
* cycle - implements IReduceInit
* iterate - implements IReduceInit
* range - implements IReduce, specialized case handles common case of all longs
* keys - iterates directly over the keys of a map, without seq or MapEntry allocation
* vals - iterates directly over the vals of a map, without seq or MapEntry allocation
* iterator-seq - creates a chunked sequence when previously it was unchunked
Additionally, hash-maps and hash-sets now provide iterators that walk
the data structure directly rather than via a sequence.
A new interface (IMapIterable) for direct key and val iterators on maps
was added. External data structures can use this interface to provide
direct key and val iterators via keys and vals.
These enhancements are particularly effective when used
in tandem with transducers via transduce, sequence, into, and
eduction.
* [CLJ-1603](http://dev.clojure.org/jira/browse/CLJ-1603)
* [CLJ-1515](http://dev.clojure.org/jira/browse/CLJ-1515)
* [CLJ-1602](http://dev.clojure.org/jira/browse/CLJ-1602)
* [CLJ-1669](http://dev.clojure.org/jira/browse/CLJ-1669)
* [CLJ-1692](http://dev.clojure.org/jira/browse/CLJ-1692)
* [CLJ-1694](http://dev.clojure.org/jira/browse/CLJ-1694)
* [CLJ-1711](http://dev.clojure.org/jira/browse/CLJ-1711)
* [CLJ-1709](http://dev.clojure.org/jira/browse/CLJ-1709)
* [CLJ-1713](http://dev.clojure.org/jira/browse/CLJ-1713)
* [CLJ-1726](http://dev.clojure.org/jira/browse/CLJ-1726)
* [CLJ-1727](http://dev.clojure.org/jira/browse/CLJ-1727)
### 2.7 Printing as data
There have been enhancements in how the REPL prints values without a
print-method, specifically Throwable and the fallthrough Object case.
Both cases now print in a tagged literal data form that can be read
by the reader.
Unhandled objects print with the class, hash code, and toString:
user=> *ns*
#object[clojure.lang.Namespace 0x55aa628 "user"]
Thrown exceptions will still be printed in the normal way by the default
REPL but printing them to a stream will show a different form:
user=> (/ 1 0)
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:158)
user=> (println *e)
#error {
:cause Divide by zero
:via
[{:type java.lang.ArithmeticException
:message Divide by zero
:at [clojure.lang.Numbers divide Numbers.java 158]}]
:trace
[[clojure.lang.Numbers divide Numbers.java 158]
[clojure.lang.Numbers divide Numbers.java 3808]
;; ... elided frames
]}
Additionally, there is a new function available to obtain a Throwable as
map data: `Throwable->map`.
* [CLJ-1703](http://dev.clojure.org/jira/browse/CLJ-1703)
* [CLJ-1716](http://dev.clojure.org/jira/browse/CLJ-1716)
* [CLJ-1735](http://dev.clojure.org/jira/browse/CLJ-1735)
### 2.8 run!
run! is a new function that takes a side effect reducing function and runs
it for all items in a collection via reduce. The accumulator is ignored and
nil is returned.
(run! println (range 10))
## 3 Enhancements
### 3.1 Error messages
* [CLJ-1261](http://dev.clojure.org/jira/browse/CLJ-1261)
Invalid defrecord results in exception attributed to consuming ns instead of defrecord ns
* [CLJ-1297](http://dev.clojure.org/jira/browse/CLJ-1297)
Give more specific hint if namespace with "-" not found to check file uses "_"
### 3.2 Documentation strings
* [CLJ-1417](http://dev.clojure.org/jira/browse/CLJ-1417)
clojure.java.io/input-stream has incorrect docstring
* [CLJ-1357](http://dev.clojure.org/jira/browse/CLJ-1357)
Fix typo in gen-class doc-string
* [CLJ-1479](http://dev.clojure.org/jira/browse/CLJ-1479)
Fix typo in filterv example
* [CLJ-1480](http://dev.clojure.org/jira/browse/CLJ-1480)
Fix typo in defmulti docstring
* [CLJ-1477](http://dev.clojure.org/jira/browse/CLJ-1477)
Fix typo in deftype docstring
* [CLJ-1478](http://dev.clojure.org/jira/browse/CLJ-1378)
Fix typo in clojure.main usage
* [CLJ-1738](http://dev.clojure.org/jira/browse/CLJ-1738)
Clarify usage on Java iterators in seq and iterator-seq
### 3.3 Performance
* [CLJ-1430](http://dev.clojure.org/jira/browse/CLJ-1430)
Improve performance of partial with more unrolling
* [CLJ-1384](http://dev.clojure.org/jira/browse/CLJ-1384)
clojure.core/set should use transients for better performance
* [CLJ-1429](http://dev.clojure.org/jira/browse/CLJ-1429)
Cache unknown multimethod value default dispatch
* [CLJ-1529](http://dev.clojure.org/jira/browse/CLJ-1529)
Reduce compile times by avoiding unnecessary calls to Class.forName()
* [CLJ-1546](http://dev.clojure.org/jira/browse/CLJ-1546)
vec is now faster on almost all inputs
* [CLJ-1618](http://dev.clojure.org/jira/browse/CLJ-1618)
set is now faster on almost all inputs
* [CLJ-1695](http://dev.clojure.org/jira/browse/CLJ-1695)
Fixed reflection call in variadic vector-of constructor
### 3.4 Other enhancements
* [CLJ-1191](http://dev.clojure.org/jira/browse/CLJ-1191)
Improve apropos to show some indication of namespace of symbols found
* [CLJ-1378](http://dev.clojure.org/jira/browse/CLJ-1378)
Hints don't work with #() form of function
* [CLJ-1498](http://dev.clojure.org/jira/browse/CLJ-1498)
Removes owner-thread check from transients - this check was preventing some valid usage of transients in core.async where a transient is created on one thread and then used again in another pooled thread (while still maintaining thread isolation).
* [CLJ-803](http://dev.clojure.org/jira/browse/CLJ-803)
Extracted IAtom interface implemented by Atom.
* [CLJ-1315](http://dev.clojure.org/jira/browse/CLJ-1315)
Don't initialize classes when importing them
* [CLJ-1330](http://dev.clojure.org/jira/browse/CLJ-1330)
Class name clash between top-level functions and defn'ed ones
* [CLJ-1349](http://dev.clojure.org/jira/browse/CLJ-1349)
Update to latest test.generative and add dependency on test.check
* [CLJ-1546](http://dev.clojure.org/jira/browse/CLJ-1546)
vec now works with things that only implement Iterable or IReduceInit
* [CLJ-1618](http://dev.clojure.org/jira/browse/CLJ-1618)
set now works with things that only implement Iterable or IReduceInit
* [CLJ-1633](http://dev.clojure.org/jira/browse/CLJ-1633)
PersistentList/creator doesn't handle ArraySeqs correctly
* [CLJ-1589](http://dev.clojure.org/jira/browse/CLJ-1589)
Clean up unused paths in InternalReduce
* [CLJ-1677](http://dev.clojure.org/jira/browse/CLJ-1677)
Add setLineNumber() to LineNumberingPushbackReader
* [CLJ-1667](http://dev.clojure.org/jira/browse/CLJ-1667)
Change test to avoid using hard-coded socket port
* [CLJ-1683](http://dev.clojure.org/jira/browse/CLJ-1683)
Change reduce tests to better catch reduce without init bugs
## 4 Bug Fixes
* [CLJ-1362](http://dev.clojure.org/jira/browse/CLJ-1362)
Reduce broken on some primitive vectors
* [CLJ-1388](http://dev.clojure.org/jira/browse/CLJ-1388)
Equality bug on records created with nested calls to map->record
* [CLJ-1274](http://dev.clojure.org/jira/browse/CLJ-1274)
Unable to set compiler options via system properties except for AOT compilation
* [CLJ-1241](http://dev.clojure.org/jira/browse/CLJ-1241)
NPE when AOTing overrided clojure.core functions
* [CLJ-1185](http://dev.clojure.org/jira/browse/CLJ-1185)
reductions does not check for reduced value
* [CLJ-1039](http://dev.clojure.org/jira/browse/CLJ-1039)
Using def with metadata {:type :anything} throws ClassCastException during printing
* [CLJ-887](http://dev.clojure.org/jira/browse/CLJ-887)
Error when calling primitive functions with destructuring in the arg vector
* [CLJ-823](http://dev.clojure.org/jira/browse/CLJ-823)
Piping seque into seque can deadlock
* [CLJ-738](http://dev.clojure.org/jira/browse/CLJ-738)
<= is incorrect when args include Double/NaN
* [CLJ-1408](http://dev.clojure.org/jira/browse/CLJ-1408)
Make cached string value of Keyword and Symbol transient
* [CLJ-1466](http://dev.clojure.org/jira/browse/CLJ-1466)
clojure.core/bean should implement Iterable
* [CLJ-1578](http://dev.clojure.org/jira/browse/CLJ-1578)
Make refer of Clojure core function not throw exception on reload
* [CLJ-1501](http://dev.clojure.org/jira/browse/CLJ-1501)
LazySeq equals() should not use equiv() logic
* [CLJ-1572](http://dev.clojure.org/jira/browse/CLJ-1572)
into (and other fns that rely on reduce) require only IReduceInit
* [CLJ-1619](http://dev.clojure.org/jira/browse/CLJ-1619)
PersistentVector now directly implements reduce without init
* [CLJ-1580](http://dev.clojure.org/jira/browse/CLJ-1580)
Transient collections should guarantee thread visibility
* [CLJ-1590](http://dev.clojure.org/jira/browse/CLJ-1590)
Some IReduce/IReduceInit implementors don't respect reduced
* [CLJ-979](http://dev.clojure.org/jira/browse/CLJ-979)
Clojure resolves to wrong deftype classes when AOT compiling or reloading
* [CLJ-1636](http://dev.clojure.org/jira/browse/CLJ-1636)
Fix intermittent SeqIterator problem by removing use of this as a sentinel
* [CLJ-1637](http://dev.clojure.org/jira/browse/CLJ-1636)
Fix regression from CLJ-1546 that broke vec on MapEntry
* [CLJ-1663](http://dev.clojure.org/jira/browse/CLJ-1663)
Fix regression from CLJ-979 for DynamicClassLoader classloader delegation
* [CLJ-1604](http://dev.clojure.org/jira/browse/CLJ-1604)
Fix error from AOT'ed code defining a var with a clojure.core symbol name
* [CLJ-1561](http://dev.clojure.org/jira/browse/CLJ-1561)
Fix incorrect line number reporting for error locations
* [CLJ-1568](http://dev.clojure.org/jira/browse/CLJ-1568)
Fix incorrect line number reporting for error locations
* [CLJ-1638](http://dev.clojure.org/jira/browse/CLJ-1638)
Fix regression from CLJ-1546 removed PersistentVector.create(List) method
* [CLJ-1681](http://dev.clojure.org/jira/browse/CLJ-1681)
Fix regression from CLJ-1248 (1.6) in reflection warning with literal nil argument
* [CLJ-1648](http://dev.clojure.org/jira/browse/CLJ-1648)
Use equals() instead of == when resolving Symbol
* [CLJ-1195](http://dev.clojure.org/jira/browse/CLJ-1195)
emit-hinted-impl expands to ns-qualified invocation of fn
* [CLJ-1237](http://dev.clojure.org/jira/browse/CLJ-1237)
reduce of sequence that switches between chunked and unchunked many times throws StackOverflow
# Changes to Clojure in Version 1.6
## CONTENTS
## 1 Compatibility and Dependencies
## 1.1 JDK Version Update
Clojure now builds with Java SE 1.6 and emits bytecode requiring Java
SE 1.6 instead of Java SE 1.5. [CLJ-1268]
## 1.2 ASM Library Update
The embedded version of the ASM bytecode library has been upgraded to
ASM 4.1. [CLJ-713]
## 1.3 Promoted "Alpha" Features
The following features are no longer marked Alpha in Clojure:
* Watches - add-watch, remove-watch
* Transients - transient, persistent!, conj!, assoc!, dissoc!, pop!, disj!
* Exception data - ex-info, ex-data
* Promises - promise, deliver
* Records - defrecord
* Types - deftype
* Pretty-print tables - print-table
## 2 New and Improved Features
### 2.1 Java API
The clojure.java.api package provides a minimal interface to bootstrap
Clojure access from other JVM languages. It does this by providing:
1. The ability to use Clojure's namespaces to locate an arbitrary var,
returning the var's clojure.lang.IFn interface.
2. A convenience method read for reading data using Clojure's edn
reader.
IFns provide complete access to Clojure's APIs. You can also access
any other library written in Clojure, after adding either its source
or compiled form to the classpath.
The public Java API for Clojure consists of the following classes and interfaces:
* clojure.java.api.Clojure
* clojure.lang.IFn
All other Java classes should be treated as implementation details,
and applications should avoid relying on them.
To look up and call a Clojure function:
IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);
Functions in clojure.core are automatically loaded. Other namespaces
can be loaded via require:
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));
IFns can be passed to higher order functions, e.g. the example below
passes plus to read:
IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
map.invoke(inc, Clojure.read("[1 2 3]"));
Most IFns in Clojure refer to functions. A few, however, refer to
non-function data values. To access these, use deref instead of fn:
IFn printLength = Clojure.var("clojure.core", "*print-length*");
Clojure.var("clojure.core", "deref").invoke(printLength);
### 2.2 Map destructuring extended to support namespaced keys
* [CLJ-1318](http://dev.clojure.org/jira/browse/CLJ-1318)
In the past, map destructuring with :keys and :syms would not work
with maps containing namespaced keys or symbols. The :keys and :syms
forms have been updated to allow them to match namespaced keys and
bind to a local variable based on the name.
Examples:
(let [m {:x/a 1, :y/b 2}
{:keys [x/a y/b]} m]
(+ a b))
(let [m {'x/a 1, 'y/b 2}
{:syms [x/a y/b]} m]
(+ a b))
Additionally, the :keys form can now take keywords instead of symbols.
This provides support specifically for auto-resolved keywords:
(let [m {:x/a 1, :y/b 2}
{:keys [:x/a :y/b]} m]
(+ a b))
(let [m {::x 1}
{:keys [::x]} m]
x)
### 2.3 New "some" operations
Many conditional functions rely on logical truth (where "falsey"
values are nil or false). Sometimes it is useful to have functions
that rely on "not nilness" instead. These functions have been added to
support these cases [CLJ-1343]:
* some? - same as (not (nil? x))
* if-some - like if-let, but checks (some? test) instead of test
* when-some - like when-let, but checks (some? test) instead of test
### 2.4 Hashing
Clojure 1.6 provides new hashing algorithms for primitives and
collections, accessible via IHashEq/hasheq (in Java) or the
clojure.core/hash function (in Clojure). In general, these changes
should be transparent to users, except hash codes used inside hashed
collections like maps and sets will have better properties.
Hash codes returned by the Java .hashCode() method are unchanged and
continue to match Java behavior or conform to the Java specification
as appropriate.
Any collections implementing IHashEq or wishing to interoperate with
Clojure collections should conform to the hashing algorithms specified
in http://clojure.org/data_structures#hash and use the new function
`mix-collection-hash` for the final mixing operation. Alternatively,
you may call the helper functions `hash-ordered-coll` and
`hash-unordered-coll`.
Any details of the current hashing algorithm not specified on that
page should be considered subject to future change.
Related tickets for dev and regressions:
* [CLJ-1328](http://dev.clojure.org/jira/browse/CLJ-1328)
Make several Clojure tests independent of ordering
* [CLJ-1331](http://dev.clojure.org/jira/browse/CLJ-1331)
Update primitive vectors to use Murmur3 hash
* [CLJ-1335](http://dev.clojure.org/jira/browse/CLJ-1335)
Update hash for empty PersistentList and LazySeq
* [CLJ-1336](http://dev.clojure.org/jira/browse/CLJ-1336)
Make hashing mixing functions available in Clojure
* [CLJ-1338](http://dev.clojure.org/jira/browse/CLJ-1338)
Make Murmur3 class public
* [CLJ-1344](http://dev.clojure.org/jira/browse/CLJ-1344)
Update mapHasheq to call Murmur3 algorithm
* [CLJ-1348](http://dev.clojure.org/jira/browse/CLJ-1348)
Add hash-ordered-coll and hash-unordered-coll
* [CLJ-1355](http://dev.clojure.org/jira/browse/CLJ-1355)
Restore cached hashCode for Symbol and (uncached) hashCode for Keyword
* [CLJ-1365](http://dev.clojure.org/jira/browse/CLJ-1365)
Add type hints for new collection hash functions
### 2.5 bitops
* [CLJ-827](http://dev.clojure.org/jira/browse/CLJ-827) - unsigned-bit-shift-right
A new unsigned-bit-shift-right (Java's >>>) has been added to the core
library. The shift distance is truncated to the least 6 bits (per the
Java specification for long >>>).
Examples:
(unsigned-bit-shift-right 2r100 1) ;; 2r010
(unsigned-bit-shift-right 2r100 2) ;; 2r001
(unsigned-bit-shift-right 2r100 3) ;; 2r000
### 2.6 clojure.test
* [CLJ-866](http://dev.clojure.org/jira/browse/CLJ-866) - test-vars
* [CLJ-1352](http://dev.clojure.org/jira/browse/CLJ-1352) - fix
regression in CLJ-866
Added a new clojure.test/test-vars function that takes a list of vars, groups them by namespace, and
runs them *with their fixtures*.
## 3 Enhancements
### 3.1 Printing
* [CLJ-908](http://dev.clojure.org/jira/browse/CLJ-908)
Print metadata for functions when *print-meta* is true and remove errant space at beginning.
* [CLJ-937](http://dev.clojure.org/jira/browse/CLJ-937)
pprint cl-format now supports E, F, and G formats for ratios.
### 3.2 Error messages
* [CLJ-1248](http://dev.clojure.org/jira/browse/CLJ-1248)
Include type information in reflection warning messages
* [CLJ-1099](http://dev.clojure.org/jira/browse/CLJ-1099)
If non-seq passed where seq is needed, error message now is an
ExceptionInfo with the instance value, retrievable via ex-data.
* [CLJ-1083](http://dev.clojure.org/jira/browse/CLJ-1083)
Fix error message reporting for "munged" function names (like a->b).
* [CLJ-1056](http://dev.clojure.org/jira/browse/CLJ-1056)
Handle more cases and improve error message for errors in defprotocol definitions.
* [CLJ-1102](http://dev.clojure.org/jira/browse/CLJ-1102)
Better handling of exceptions with empty stack traces.
* [CLJ-939](http://dev.clojure.org/jira/browse/CLJ-939)
Exceptions thrown in the top level ns form are reported without file or line number.
### 3.3 Documentation strings
* [CLJ-1164](http://dev.clojure.org/jira/browse/CLJ-1164)
Fix typos in clojure.instant/validated and other internal instant functions.
* [CLJ-1143](http://dev.clojure.org/jira/browse/CLJ-1143)
Correct doc string for ns macro.
* [CLJ-196](http://dev.clojure.org/jira/browse/CLJ-196)
Clarify value of *file* is undefined in the REPL.
* [CLJ-1228](http://dev.clojure.org/jira/browse/CLJ-1228)
Fix a number of spelling errors in namespace and doc strings.
* [CLJ-835](http://dev.clojure.org/jira/browse/CLJ-835)
Update defmulti doc to clarify expectations for hierarchy argument.
* [CLJ-1304](http://dev.clojure.org/jira/browse/CLJ-1304)
Fix minor typos in documentation and comments
* [CLJ-1302](http://dev.clojure.org/jira/browse/CLJ-1302)
Mention that keys and vals order are consistent with seq order
### 3.4 Performance
* [CLJ-858](http://dev.clojure.org/jira/browse/CLJ-858)
Improve speed of STM by removing System.currentTimeMillis.
* [CLJ-669](http://dev.clojure.org/jira/browse/CLJ-669)
clojure.java.io/do-copy: use java.nio for Files
* [commit](https://github.com/clojure/clojure/commit/0b73494c3c855e54b1da591eeb687f24f608f346)
Reduce overhead of protocol callsites by removing unneeded generated
cache fields.
### 3.5 Other enhancements
* [CLJ-908](http://dev.clojure.org/jira/browse/CLJ-908)
Make *default-data-reader-fn* set!-able in REPL, similar to *data-readers*.
* [CLJ-783](http://dev.clojure.org/jira/browse/CLJ-783)
Make clojure.inspector/inspect-tree work on sets.
* [CLJ-896](http://dev.clojure.org/jira/browse/CLJ-896)
Make browse-url aware of xdg-open.
* [CLJ-1160](http://dev.clojure.org/jira/browse/CLJ-1160)
Fix clojure.core.reducers/mapcat does not stop on reduced? values.
* [CLJ-1121](http://dev.clojure.org/jira/browse/CLJ-1121)
-> and ->> have been rewritten to work with a broader set of macros.
* [CLJ-1105](http://dev.clojure.org/jira/browse/CLJ-1105)
clojure.walk now supports records.
* [CLJ-949](http://dev.clojure.org/jira/browse/CLJ-949)
Removed all unnecessary cases of sneakyThrow.
* [CLJ-1238](http://dev.clojure.org/jira/browse/CLJ-1238)
Allow EdnReader to read foo// (matches LispReader behavior).
* [CLJ-1264](http://dev.clojure.org/jira/browse/CLJ-1264)
Remove uses of _ as a var in the Java code (causes warning in Java 8).
* [CLJ-394](http://dev.clojure.org/jira/browse/CLJ-394)
Add record? predicate.
* [CLJ-1200](http://dev.clojure.org/jira/browse/CLJ-1200)
ArraySeq dead code cleanup, ArraySeq_short support added.
* [CLJ-1331](http://dev.clojure.org/jira/browse/CLJ-1331)
Primitive vectors should implement hasheq and use new hash algorithm
* [CLJ-1354](http://dev.clojure.org/jira/browse/CLJ-1354)
Make APersistentVector.SubVector public so other collections can access
* [CLJ-1353](http://dev.clojure.org/jira/browse/CLJ-1353)
Make awt run headless during the build process
## 4 Bug Fixes
* [CLJ-1018](http://dev.clojure.org/jira/browse/CLJ-1018)
Make range consistently return infinite sequence of start with a step of 0.
* [CLJ-863](http://dev.clojure.org/jira/browse/CLJ-863)
Make interleave return () on 0 args and identity on 1 args.
* [CLJ-1072](http://dev.clojure.org/jira/browse/CLJ-1072)
Update internal usages of the old metadata reader syntax to new syntax.
* [CLJ-1193](http://dev.clojure.org/jira/browse/CLJ-1193)
Make bigint and biginteger functions work on double values outside long range.
* [CLJ-1154](http://dev.clojure.org/jira/browse/CLJ-1154)
Make Compile.java flush but not close stdout so errors can be reported.
* [CLJ-1161](http://dev.clojure.org/jira/browse/CLJ-1161)
Remove bad version.properties from sources jar.
* [CLJ-1175](http://dev.clojure.org/jira/browse/CLJ-1175)
Fix invalid behavior of Delay/deref if an exception is thrown - exception will
now be rethrown on subsequent calls and not enter a corrupted state.
* [CLJ-1171](http://dev.clojure.org/jira/browse/CLJ-1171)
Fix several issues with instance? to make it consistent when used with apply.
* [CLJ-1202](http://dev.clojure.org/jira/browse/CLJ-1202)
Protocol fns with dashes may get incorrectly compiled into field accesses.
* [CLJ-850](http://dev.clojure.org/jira/browse/CLJ-850)
Add check to emit invokePrim with return type of double or long if type-hinted.
* [CLJ-1177](http://dev.clojure.org/jira/browse/CLJ-1177)
clojure.java.io URL to File coercion corrupts path containing UTF-8 characters.
* [CLJ-1234](http://dev.clojure.org/jira/browse/CLJ-1234)
Accept whitespace in Record and Type reader forms (similar to data literals).
* [CLJ-1233](http://dev.clojure.org/jira/browse/CLJ-1233)
Allow ** as a valid symbol name without triggering dynamic warnings.
* [CLJ-1246](http://dev.clojure.org/jira/browse/CLJ-1246)
Add support to clojure.reflect for classes with annotations.
* [CLJ-1184](http://dev.clojure.org/jira/browse/CLJ-1184)
Evaling #{do ...} or [do ...] is treated as do special form.
* [CLJ-1090](http://dev.clojure.org/jira/browse/CLJ-1090)
Indirect function calls through Var instances fail to clear locals.
* [CLJ-1076](http://dev.clojure.org/jira/browse/CLJ-1076)
pprint tests fail on Windows, expecting \n.
* [CLJ-766](http://dev.clojure.org/jira/browse/CLJ-766)
Make into-array work consistently with short-array and byte-array on
bigger types.
* [CLJ-1285](http://dev.clojure.org/jira/browse/CLJ-1285)
Data structure invariants are violated after persistent operations when
collision node created by transients.
* [CLJ-1222](http://dev.clojure.org/jira/browse/CLJ-1222)
Multiplication overflow issues around Long/MIN_VALUE
* [CLJ-1118](http://dev.clojure.org/jira/browse/CLJ-1118)
Inconsistent numeric comparison semantics between BigDecimals and other numerics
* [CLJ-1125](http://dev.clojure.org/jira/browse/CLJ-1125)
Clojure can leak memory in a servlet container when using dynamic
bindings or STM transactions.
* [CLJ-1082](http://dev.clojure.org/jira/browse/CLJ-1082)
Subvecs of primitve vectors cannot be reduced
* [CLJ-1301](http://dev.clojure.org/jira/browse/CLJ-1301)
Case expressions use a mixture of hashCode and hasheq, potentially
leading to missed case matches when these differ.
* [CLJ-983](http://dev.clojure.org/jira/browse/CLJ-983)
proxy-super does not restore original binding if call throws exception
* [CLJ-1176](http://dev.clojure.org/jira/browse/CLJ-1176)
clojure.repl/source errors when *read-eval* bound to :unknown
* [CLJ-935](http://dev.clojure.org/jira/browse/CLJ-935)
clojure.string/trim uses different definition of whitespace than
triml and trimr
* [CLJ-1058](http://dev.clojure.org/jira/browse/CLJ-1058)
StackOverflowError on exception in reducef for PersistentHashMap
fold
* [CLJ-1328](http://dev.clojure.org/jira/browse/CLJ-1328)
Fix some tests in the Clojure test suite to make their names unique
and independent of hashing order
* [CLJ-1339](http://dev.clojure.org/jira/browse/CLJ-1339)
Empty primitive vectors throw NPE on .equals with non-vector
sequential types
* [CLJ-1363](http://dev.clojure.org/jira/browse/CLJ-1363)
Field access via .- in reflective case does not work
* [CLJ-944](http://dev.clojure.org/jira/browse/CLJ-944)
Compiler gives constant collections types which mismatch their
runtime values
* [CLJ-1387](http://dev.clojure.org/jira/browse/CLJ-1387)
reduce-kv on large hash maps ignores reduced result
# Changes to Clojure in Version 1.5.1
* fix for leak caused by ddc65a96fdb1163b
# Changes to Clojure in Version 1.5
## CONTENTS
1 Deprecated and Removed Features
1.1 Clojure 1.5 reducers library requires Java 6 or later
2 New and Improved Features
2.1 Reducers
2.2 Reader Literals improved
2.3 clojure.core/set-agent-send-executor!, set-agent-send-off-executor!, and send-via
2.4 New threading macros
2.5 Column metadata captured by reader
2.6 gen-class improvements
2.7 Support added for marker protocols
2.8 clojure.pprint/print-table output compatible with Emacs Org mode
2.9 clojure.string/replace and replace-first handle special characters more predictably
2.10 Set and map constructor functions allow duplicates
2.11 More functions preserve metadata
2.12 New edn reader, improvements to *read-eval*
3 Performance Enhancements
4 Improved error messages
5 Improved documentation strings
6 Bug Fixes
7 Binary Compatibility Notes
## 1 Deprecated and Removed Features
### 1.1 Clojure 1.5 reducers library requires Java 6 or later
The new reducers library (see below) requires Java 6 plus a ForkJoin
library, or Java 7 or later. Clojure 1.5 can still be compiled and
run with Java 5. The only limitations with Java 5 are that the new
reducers library will not work, and building Clojure requires skipping
the test suite (e.g. by using the command "ant jar").
## 2 New and Improved Features
### 2.1 Reducers
Reducers provide a set of high performance functions for working with collections. The actual fold/reduce algorithms are specified via the collection being reduced. This allows each collection to define the most efficient way to reduce its contents.
The implementation details of reducers are available at the [Clojure blog](http://clojure.com/blog/2012/05/08/reducers-a-library-and-model-for-collection-processing.html) and therefore won't be repeated in these change notes. However, as a summary:
* There is a new namespace: clojure.core.reducers
* It contains new versions of map, filter etc based upon transforming reducing functions - reducers
* It contains a new function, fold, which is a parallel reduce+combine
fold uses fork/join when working with (the existing!) Clojure vectors and maps
* Your new parallel code has exactly the same shape as your existing seq-based code
* The reducers are composable
* Reducer implementations are primarily functional - no iterators
* The model uses regular data structures, not 'parallel collections' or other OO malarkey
* It's fast, and can become faster still
* This is work-in-progress
Examples:
user=> (require '[clojure.core.reducers :as r])
user=> (reduce + (r/filter even? (r/map inc [1 1 1 2])))
;=> 6
;;red is a reducer awaiting a collection
user=> (def red (comp (r/filter even?) (r/map inc)))
user=> (reduce + (red [1 1 1 2]))
;=> 6
user=> (into #{} (r/filter even? (r/map inc [1 1 1 2])))
;=> #{2}
### 2.2 Reader Literals improved
* [CLJ-1034](http://dev.clojure.org/jira/browse/CLJ-1034)
"Conflicting data-reader mapping" should no longer be thrown where there really isn't a conflict. Until this patch, having data_readers.clj on the classpath twice would cause the above exception.
* [CLJ-927](http://dev.clojure.org/jira/browse/CLJ-927)
Added `*default-data-reader-fn*` to clojure.core. When no data reader is found for a tag and `*default-data-reader-fn*`is non-nil, it will be called with two arguments, the tag and the value. If `*default-data-reader-fn*` is nil (the default), an exception will be thrown for the unknown tag.
### 2.3 clojure.core/set-agent-send-executor!, set-agent-send-off-executor!, and send-via
Added two new functions:
* clojure.core/set-agent-send-executor!
Allows the user to set the `java.util.concurrent.Executor` used when calling `clojure.core/send`. Defaults to a fixed thread pool of size: (numCores + 2)
* clojure.core/set-agent-send-off-executor!
Allows the user to set the `java.util.concurrent.Executor` used when calling `clojure.core/send-off`. Defaults to a cached thread pool.
* clojure.core/send-via
Like `send`, and `send-off`, except the first argument to this function is an executor to use when sending.
### 2.4 New threading macros
* clojure.core/cond-> [expr & clauses]
Takes an expression and a set of test/form pairs. Threads the expression (via ->) through each form for which the corresponding test expression (not threaded) is true.
Example:
user=> (cond-> 1
true inc
false (* 42)
(= 2 2) (* 3))
6
* clojure.core/cond->> [expr & clauses]
Takes an expression and a set of test/form pairs. Threads expr (via ->>)
through each form for which the corresponding test expression (not threaded) is true.
Example:
user=> (def d [0 1 2 3])
#'user/d
user=> (cond->> d
true (map inc)
(seq? d) (map dec)
(= (count d) 4) (reduce +)) ;; no threading in the test expr
;; so d must be passed in explicitly
10
* clojure.core/as-> [expr name & forms]
Binds name to expr, evaluates the first form in the lexical context of that binding, then binds name to that result, repeating for each successive form
Note: this form does not actually perform any threading. Instead it allows the user to assign a name and lexical context to a value created by a parent threading form.
Example:
user=> (-> 84
(/ 4)
(as-> twenty-one ;; uses the value from ->
(* 2 twenty-one))) ;; no threading here
42
* clojure.core/some-> [expr & forms]
When expr is not nil, threads it into the first form (via ->),
and when that result is not nil, through the next etc.
Example:
user=> (defn die [x] (assert false))
#'user/die
user=> (-> 1 inc range next next next die)
AssertionError Assert failed: false user/die (NO_SOURCE_FILE:65)
user=> (some-> 1 inc range next next next die)
nil
* clojure.core/some->> [expr & forms]
When expr is not nil, threads it into the first form (via ->>),
and when that result is not nil, through the next etc.
Same as some-> except the value is threaded as the last argument in each form.
### 2.5 Column metadata captured by reader
* [CLJ-960](http://dev.clojure.org/jira/browse/CLJ-960)
Data read by the clojure reader is now tagged with :column in addition to :line.
### 2.6 gen-class improvements
* [CLJ-745](http://dev.clojure.org/jira/browse/CLJ-745)
It is now possible to expose protected final methods via `:exposes-methods` in `gen-class`. This allows Clojure classes created via gen-class to access protected methods of its parent class.
Example:
(gen-class :name clojure.test_clojure.genclass.examples.ProtectedFinalTester
:extends java.lang.ClassLoader
:main false
:prefix "pf-"
:exposes-methods {findSystemClass superFindSystemClass})
* [CLJ-948](http://dev.clojure.org/jira/browse/CLJ-948)
It is now possible to annotate constructors via `gen-class`.
Example:
(gen-class :name foo.Bar
:extends clojure.lang.Box
:constructors {^{Deprecated true} [Object] [Object]}
:init init
:prefix "foo")
### 2.7 Support added for marker protocols
* [CLJ-966](http://dev.clojure.org/jira/browse/CLJ-966)
`defprotocol` no longer requires that at least one method be given in the definition of the protocol. This allows for marker protocols, whose sole reason of existence is to allow `satisfies?` to be true for a given type.
Example:
user=> (defprotocol P (hi [_]))
P
user=> (defprotocol M) ; marker protocol
M
user=> (deftype T [a] M P (hi [_] "hi there"))
user.T
user=> (satisfies? P (T. 1))
true
user=> (satisfies? M (T. 1))
true
user=> (hi (T. 1))
"hi there"
user=> (defprotocol M2 "marker for 2") ; marker protocol again
M2
user=> (extend-type T M2)
nil
user=> (satisfies? M2 (T. 1))
true
### 2.8 clojure.pprint/print-table output compatible with Emacs Org mode
For the convenience of those that use Emacs Org mode,
`clojure.pprint/print-table` now prints tables in the form used by
that mode. Emacs Org mode has features to make it easy to edit such
tables, and even to do spreadsheet-like calculations on their
contents. See the [Org mode documentation on
tables](http://orgmode.org/manual/Tables.html) for details.
user=> (clojure.pprint/print-table [:name :initial-impression]
[{:name "Rich" :initial-impression "rock star"}
{:name "Andy" :initial-impression "engineer"}])
| :name | :initial-impression |
|-------+---------------------|
| Rich | rock star |
| Andy | engineer |
### 2.9 clojure.string/replace and replace-first handle special characters more predictably
`clojure.string/replace` and `clojure.string/replace-first` are now
consistent in the way that they handle the replacement strings: all
characters in the replacement strings are treated literally, including
backslash and dollar sign characters.
user=> (require '[clojure.string :as s])
user=> (s/replace-first "munge.this" "." "$")
;=> "munge$this"
user=> (s/replace "/my/home/dir" #"/" (fn [s] "\\"))
;=> "\\my\\home\\dir"
There is one exception, which is described in the doc strings. If you
call these functions with a regex to search for and a string as the
replacement, then dollar sign and backslash characters in the
replacement string are treated specially. Occurrences of `$1` in the
replacement string are replaced with the string that matched the first
parenthesized subexpression of the regex, occurrences of `$2` are
replaced with the match of the second parenthesized subexpression,
etc.
user=> (s/replace "x12, b4" #"([a-z]+)([0-9]+)" "$1 <- $2")
;=> "x <- 12, b <- 4"
Individual occurrences of `$` or `\` in the replacement string that
you wish to be treated literally can be escaped by prefixing them with
a `\`. If you wish your replacement string to be treated literally
and its contents are unknown to you at compile time (or you don't wish
to tarnish your constant string with lots of backslashes), you can use
the new function `clojure.string/re-quote-replacement` to do the
necessary escaping of special characters for you.
user=> (s/replace "x12, b4" #"([a-z]+)([0-9]+)"
(s/re-quote-replacement "$1 <- $2"))
;=> "$1 <- $2, $1 <- $2"
### 2.10 Set and map constructor functions allow duplicates
All of the functions that construct sets such as `set` and
`sorted-set` allow duplicate elements to appear in their arguments,
and they are documented to treat this case as if by repeated uses of
`conj`.
Similarly, all map constructor functions such as `hash-map`,
`array-map`, and `sorted-map` allow duplicate keys, and are documented
to treat this case as if by repeated uses of `assoc`.
As before, literal sets, e.g. `#{1 2 3}`, do not allow duplicate
elements, and while elements can be expressions evaluated at run time
such as `#{(inc x) (dec y)}`, this leads to a check for duplicates at
run time whenever the set needs to be constructed, throwing an
exception if any duplicates are found.
Similarly, literal maps do not allow duplicate keys. New to Clojure
1.5 is a performance optimization: if all keys are compile time
constants but one or more values are expressions requiring evaluation
at run time, duplicate keys are checked for once at compile time only,
not each time a map is constructed at run time.
* [CLJ-1065](http://dev.clojure.org/jira/browse/CLJ-1065)
Allow duplicate set elements and map keys for all set and map constructors
### 2.11 More functions preserve metadata
Most functions that take a collection and return a "modified" version
of that collection preserve the metadata that was on the input
collection, e.g. `conj`, `assoc`, `dissoc`, etc. One notable
exception was `into`, which would return a collection with metadata
`nil` for several common types of input collections.
Now the functions `into`, `select-keys`, `clojure.set/project`, and
`clojure.set/rename` return collections with the same metadata as
their input collections.
### 2.12 New edn reader, improvements to `*read-eval*`
The new `clojure.edn` namespace reads edn (http://edn-format.org) data,
and should be used for reading data from untrusted sources.
Clojure's core read* functions can evaluate code, and should not be
used to read data from untrusted sources. As of 1.5, `*read-eval*`
supports a documented set of thread-local bindings, see the doc string
for details.
`*read-eval*`'s default can be set to false by setting a system property:
-Dclojure.read.eval=false
## 3 Performance and Memory Enhancements
* [CLJ-988](http://dev.clojure.org/jira/browse/CLJ-988)
Multimethod tables are now protected by a read/write lock instead of a synchronized method. This should result in a performance boost for multithreaded code using multimethods.
* [CLJ-1061](http://dev.clojure.org/jira/browse/CLJ-1061)
`when-first` now evaluates its expression only once.
* [CLJ-1084](http://dev.clojure.org/jira/browse/CLJ-1084)
`PersistentVector$ChunkedSeq` now implements `Counted` interface, to avoid some cases where vector elements were being counted by iterating over their elements.
* [CLJ-867](http://dev.clojure.org/jira/browse/CLJ-867)
Records with same fields and field values, but different types, now usually hash to different values.
* [CLJ-1000](http://dev.clojure.org/jira/browse/CLJ-1000)
Cache hasheq() for seqs, sets, vectors, maps and queues
* (no ticket) array-map perf tweaks
* [CLJ-1111](http://dev.clojure.org/jira/browse/CLJ-1111)
Allows loop to evaluate to primitive values
* (no ticket) Move loop locals into same clearing context as loop body
## 4 Improved error messages
* [CLJ-103](http://dev.clojure.org/jira/browse/CLJ-103)
Improved if-let error message when form has a improperly defined body.
* [CLJ-897](http://dev.clojure.org/jira/browse/CLJ-897)
Don't use destructuring in defrecord/deftype arglists to get a slightly better error message when forgetting to specify the fields vector
* [CLJ-788](http://dev.clojure.org/jira/browse/CLJ-788)
Add source and line members and getters to CompilerException
* [CLJ-157](http://dev.clojure.org/jira/browse/CLJ-157)
Better error messages for syntax errors w/ defn and fn
* [CLJ-940](http://dev.clojure.org/jira/browse/CLJ-940)
Passing a non-sequence to refer :only results in uninformative exception
* [CLJ-1052](http://dev.clojure.org/jira/browse/CLJ-1052)
`assoc` now throws an exception if the last key argument is missing a value.
## 5 Improved documentation strings
* [CLJ-893](http://dev.clojure.org/jira/browse/CLJ-893)
Document that vec will alias Java arrays
* [CLJ-892](http://dev.clojure.org/jira/browse/CLJ-892)
Clarify doc strings of sort and sort-by: they will modify Java array arguments
* [CLJ-1019](http://dev.clojure.org/jira/browse/CLJ-1019)
ns-resolve doc has a typo
* [CLJ-1038](http://dev.clojure.org/jira/browse/CLJ-1038)
Docstring for deliver doesn't match behavior
* [CLJ-1055](http://dev.clojure.org/jira/browse/CLJ-1055)
"be come" should be "become"
* [CLJ-917](http://dev.clojure.org/jira/browse/CLJ-917)
clojure.core/definterface is not included in the API docs
* (no ticket) clojure.core/read, read-string, and *read-eval* all have more extensive documentation.
## 6 Bug Fixes
* [CLJ-962](http://dev.clojure.org/jira/browse/CLJ-962)
Vectors returned by subvec allow access at negative indices
* [CLJ-952](http://dev.clojure.org/jira/browse/CLJ-952)
bigdec does not properly convert a clojure.lang.BigInt
* [CLJ-975](http://dev.clojure.org/jira/browse/CLJ-975)
inconsistent destructuring behaviour when using nested maps
* [CLJ-954](http://dev.clojure.org/jira/browse/CLJ-954)
TAP support in clojure.test.tap Needs Updating
* [CLJ-881](http://dev.clojure.org/jira/browse/CLJ-881)
exception when cl-format is given some ~f directive/value combinations
* [CLJ-763](http://dev.clojure.org/jira/browse/CLJ-763)
Do not check for duplicates in destructuring map creation
* [CLJ-667](http://dev.clojure.org/jira/browse/CLJ-667)
Allow loops fully nested in catch/finally
* [CLJ-768](http://dev.clojure.org/jira/browse/CLJ-768)
cl-format bug in ~f formatting
* [CLJ-844](http://dev.clojure.org/jira/browse/CLJ-844)
NPE calling keyword on map from bean
* [CLJ-934](http://dev.clojure.org/jira/browse/CLJ-934)
disj! Throws exception when attempting to remove multiple items in one call
* [CLJ-943](http://dev.clojure.org/jira/browse/CLJ-943)
When load-lib fails, a namespace is still created
* [CLJ-981](http://dev.clojure.org/jira/browse/CLJ-981)
clojure.set/rename-keys deletes keys when there's a collision
* [CLJ-961](http://dev.clojure.org/jira/browse/CLJ-961)
with-redefs loses a Var's root binding if the Var is thread-bound
* [CLJ-1032](http://dev.clojure.org/jira/browse/CLJ-1032)
seque leaks threads from the send-off pool
* [CLJ-1041](http://dev.clojure.org/jira/browse/CLJ-1041)
reduce-kv on sorted maps should stop on seeing a Reduced value
* [CLJ-1011](http://dev.clojure.org/jira/browse/CLJ-1011)
clojure.data/diff should cope with null and false values in maps
* [CLJ-977](http://dev.clojure.org/jira/browse/CLJ-977)
(int \a) returns a value, (long \a) throws an exception
* [CLJ-964](http://dev.clojure.org/jira/browse/CLJ-964)
test-clojure/rt.clj has undeclared dependency on clojure.set
* [CLJ-923](http://dev.clojure.org/jira/browse/CLJ-923)
Reading ratios prefixed by + is not working
* [CLJ-1012](http://dev.clojure.org/jira/browse/CLJ-1012)
partial function should also accept 1 arg (just f)
* [CLJ-932](http://dev.clojure.org/jira/browse/CLJ-932)
contains? Should throw exception on non-keyed collections
* [CLJ-730](http://dev.clojure.org/jira/browse/CLJ-730) Create test suite for functional fns (e.g. juxt, comp, partial, etc.)
* [CLJ-757](http://dev.clojure.org/jira/browse/CLJ-757)
Empty transient maps/sets return wrong value for .contains
* [CLJ-828](http://dev.clojure.org/jira/browse/CLJ-828)
clojure.core/bases returns a cons when passed a class and a Java array when passed an interface
* [CLJ-1062](http://dev.clojure.org/jira/browse/CLJ-1062)
CLJ-940 breaks compilation of namespaces that don't have any public functions
* [CLJ-1070](http://dev.clojure.org/jira/browse/CLJ-1070)
PersistentQueue's hash function does not match its equality
* [CLJ-987](http://dev.clojure.org/jira/browse/CLJ-987)
pprint doesn't flush the underlying stream
* [CLJ-963](http://dev.clojure.org/jira/browse/CLJ-963)
Support pretty printing namespace declarations under code-dispatch
* [CLJ-902](http://dev.clojure.org/jira/browse/CLJ-902)
doc macro broken for namespaces
* [CLJ-909](http://dev.clojure.org/jira/browse/CLJ-909) Make LineNumberingPushbackReader's buffer size configurable
* [CLJ-910](http://dev.clojure.org/jira/browse/CLJ-910) Allow for type-hinting the method receiver in memfn
* [CLJ-1048](http://dev.clojure.org/jira/browse/CLJ-1048) add test.generative to Clojure's tests
* [CLJ-1071](http://dev.clojure.org/jira/browse/CLJ-1071) ExceptionInfo does no abstraction
* [CLJ-1085](http://dev.clojure.org/jira/browse/CLJ-1085) clojure.main/repl unconditionally refers REPL utilities into `*ns*`
* (no ticket) Rich Hickey fix: syntax-quote was walking records, returning maps
* [CLJ-1116](http://dev.clojure.org/jira/browse/CLJ-1116) More REPL-friendly 'ns macro
* (no ticket) Rich Hickey fix: deref any j.u.c.Future
* [CLJ-1092](http://dev.clojure.org/jira/browse/CLJ-1092) New function re-quote-replacement has incorrect :added metadata
* [CLJ-1098](http://dev.clojure.org/jira/browse/CLJ-1098) Implement IKVReduce and CollFold for nil
* (no ticket) Rich Hickey fix: impose once semantics on fabricated closures for e.g. loops
* [CLJ-1140](http://dev.clojure.org/jira/browse/CLJ-1140) Restore {:as x} destructuring for empty lists
* [CLJ-1150](http://dev.clojure.org/jira/browse/CLJ-1150) Make some PersistentVector's and APersistentVector.SubVector's internals public
* (no ticket) Rich Hickey fix: use non-loading classForName
* [CLJ-1106](http://dev.clojure.org/jira/browse/CLJ-1106) Fixing set equality
## 7 Binary Compatibility Notes
* `public static inner class LispReader.ReaderException(int line, Throwable cause)`
Constructor changed to `ReaderException(int line, int column, Throwable cause)`
* `public Object clojure.lang.Agent.dispatch(IFn fn, ISeq args, boolean solo)`
Replaced with `dispatch(IFn fn, ISeq args, Executor exec)`
# Changes to Clojure in Version 1.4
## CONTENTS
1 Deprecated and Removed Features
1.1 Fields that Start With a Dash Can No Longer Be Accessed Using Dot Syntax
2 New/Improved Features
2.1 Reader Literals
2.2 clojure.core/mapv
2.3 clojure.core/filterv
2.4 clojure.core/ex-info and clojure.core/ex-data
2.5 clojure.core/reduce-kv
2.6 clojure.core/contains? Improved
2.7 clojure.core/min and clojure.core/max prefer NaN
2.8 clojure.java.io/as-file and clojure.java.io/as-url Handle URL-Escaping Better
2.9 New Dot Syntax for Record and Type Field Access
2.10 Record Factory Methods Available Inside defrecord
2.11 assert-args Displays Namespace and Line Number on Errors
2.12 File and Line Number Added to Earmuff Dynamic Warning
2.13 require Can Take a :refer Option
2.14 *compiler-options* Var
2.15 Improved Reporting of Invalid Characters in Unicode String Literals
2.16 clojure.core/hash No Longer Relies on .hashCode
2.17 Java 7 Documentation
2.18 loadLibrary Loads Library Using System ClassLoader
2.19 Java int is boxed as java.lang.Integer
3 Performance Enhancements
4 Bug Fixes
## 1 Deprecated and Removed Features
### 1.1 Record and Type Fields that Start With a Dash Can No Longer Be Accessed Using Dot Syntax
Clojure 1.4 introduces a field accessor syntax for the dot special form that aligns Clojure field lookup syntax with ClojureScript's.
For example, in Clojure 1.3, one can declare a record with a field starting with dash and access it like this:
(defrecord Bar [-a]) ;=> user.Bar
(.-a (Bar. 10)) ;=> 10
In 1.4, the above code results in `IllegalArgumentException No matching field found: a for class user.Bar`
However, the field may still be accessed as a keyword:
(:-a (Bar. 10)) ;=> 10
## 2 New and Improved Features
### 2.1 Reader Literals
Clojure 1.4 supports reader literals, which are data structures tagged
by a symbol to denote how they will be read.
When Clojure starts, it searches for files named `data_readers.clj`
at the root of the classpath. Each such file must contain a Clojure
map of symbols, like this:
{foo/bar my.project.foo/bar
foo/baz my.project/baz}
The key in each pair is a tag that will be recognized by
the Clojure reader. The value in the pair is the
fully-qualified name of a Var which will be invoked by the reader to
parse the form following the tag. For example, given the
data_readers.clj file above, the Clojure reader would parse this
form:
#foo/bar [1 2 3]
by invoking the Var `#'my.project.foo/bar` on the vector `[1 2 3]`. The
data reader function is invoked on the form AFTER it has been read
as a normal Clojure data structure by the reader.
Reader tags without namespace qualifiers are reserved for Clojure. Default
reader tags are defined in `clojure.core/default-data-readers` but may be
overridden in `data_readers.clj` or by rebinding `*data-readers*`.
#### 2.1.1 Instant Literals
Clojure supports literals for instants in the form
`#inst "yyyy-mm-ddThh:mm:ss.fff+hh:mm"`. These literals are parsed as `java.util.Date`s
by default. They can be parsed as `java.util.Calendar`s or `java.util.Timestamp`s
by binding `*data-readers*` to use `clojure.instant/read-instant-calendar` or
`clojure.instant/read-instant-timestamp`.
(def instant "#inst \"@2010-11-12T13:14:15.666\"")
; Instants are read as java.util.Date by default
(= java.util.Date (class (read-string instant)))
;=> true
; Instants can be read as java.util.Calendar or java.util.Timestamp
(binding [*data-readers* {'inst read-instant-calendar}]
(= java.util.Calendar (class (read-string instant))))
;=> true
(binding [*data-readers* {'inst read-instant-timestamp}]
(= java.util.Timestamp (class (read-string instant))))
;=> true
#### 2.1.2 UUID Literals
Clojure supports literals for UUIDs in the form `#uuid "uuid-string"`. These
literals are parsed as `java.util.UUID`s.
### 2.2 clojure.core/mapv
`mapv` takes a function `f` and one or more collections and returns a
vector consisting of the result of applying `f` to the set of first items of
each collection, followed by applying `f` to the set of second items in each
collection, until any one of the collections is exhausted. Any remaining
items in other collections are ignored. `f` should accept a number of arguments
equal to the number of collections.
(= [1 2 3] (mapv + [1 2 3]))
;=> true
(= [2 3 4] (mapv + [1 2 3] (repeat 1)))
;=> true
### 2.3 clojure.core/filterv
`filterv` takes a predicate `pred` and a collection and returns a vector
of the items in the collection for which `(pred item)` returns true. `pred`
must be free of side-effects.
(= [] (filterv even? [1 3 5]))
;=> true
(= [2 4] (filterv even? [1 2 3 4 5]))
;=> true
### 2.4 clojure.core/ex-info and clojure.core/ex-data
`ex-info` creates an instance of `ExceptionInfo`. `ExceptionInfo` is a
`RuntimeException` subclass that takes a string `msg` and a map of data.
(ex-info "Invalid use of robots" {:robots false})
;=> #
`ex-data` is called with an exception and will retrieve that map of data
if the exception is an instance of `ExceptionInfo`.
(ex-data (ex-info "Invalid use of robots" {:robots false}))
;=> {:robots false}
### 2.5 clojure.core/reduce-kv
`reduce-kv` reduces an associative collection. It takes a function `f`,
an initial value `init` and an associative collection `coll`. `f` should
be a function of 3 arguments. Returns the result of applying `f` to `init`,
the first key and the first value in `coll`, then applying `f` to that result
and the 2nd key and value, etc. If `coll` contains no entries, returns `init`
and f is not called. Note that `reduce-kv` is supported on vectors,
where the keys will be the ordinals.
(reduce-kv str "Hello " {:w \o :r \l :d \!})
;=> "Hello :rl:d!:wo"
(reduce-kv str "Hello " [\w \o \r \l \d \!])
;=> "Hello 0w1o2r3l4d5!"
### 2.6 clojure.core/contains? Improved
`contains?` now works with `java.util.Set`.
### 2.7 clojure.core/min and clojure.core/max prefer NaN
`min` and `max` now give preference to returning NaN if either of their
arguments is NaN.
### 2.8 clojure.java.io/as-file and clojure.java.io/as-url Handle URL-Escaping Better
`as-file` and `as-url` now handle URL-escaping in both directions.
### 2.9 New Dot Syntax for Record and Type Field Access
Clojure 1.4 introduces a field accessor syntax for the dot special
form that aligns Clojure field lookup syntax with ClojureScript's.
In 1.4, to declare a record type and access its property `x`, one can
write:
(defrecord Foo [x]) ;=> user.Foo
(.-x (Foo. 10)) ;=> 10
This addition makes it easier to write code that will run as expected
in both Clojure and ClojureScript.
### 2.10 Record Factory Methods Available Inside defrecord
Prior to 1.4, you could not use the factory functions (`->RecordClass`
and `map->RecordClass`) to construct a new record from inside a
`defrecord` definition.
The following example did not work prior to 1.4, but is now
valid. This example makes use of `->Mean` which would have not yet
been available.
(defrecord Mean [last-winner]
Player
(choose [_] (if last-winner last-winner (random-choice)))
(update-strategy [_ me you] (->Mean (when (iwon? me you) me))))
### 2.11 assert-args Displays Namespace and Line Number on Errors
`assert-args` now uses &form to report the namespace and line number where
macro syntax errors occur.
### 2.12 File and Line Number Added to Earmuff Dynamic Warning
When a variable is defined using earmuffs but is not declared dynamic,
Clojure emits a warning. That warning now includes the file and line
number.
### 2.13 require Can Take a :refer Option
`require` can now take a `:refer` option. `:refer` takes a list of symbols
to refer from the namespace or `:all` to bring in all public vars.
### 2.14 \*compiler-options\* Var
The dynamic var `*compiler-options*` contains a map of options to send
to the Clojure compiler.
Supported options:
* `:elide-meta`: Have certain metadata elided during compilation. This
should be set to a collection of keywords.
* `:disable-locals-clearing`: Set to true to disable clearing. Useful for
using a debugger.
The main function of the Clojure compiler sets the
`*compiler-options*` from properties prefixed by `clojure.compiler`,
e.g.
java -Dclojure.compiler.elide-meta='[:doc :file :line]'
### 2.15 Improved Reporting of Invalid Characters in Unicode String Literals
When the reader finds an invalid character in a Unicode string literal, it
now reports the character instead of its numerical representation.
### 2.16 clojure.core/hash No Longer Relies on .hashCode
`hash` no longer directly uses .hashCode() to return the hash of a Clojure
data structure. It calls `clojure.lang.Util.hasheq`, which has its own implementation
for Integer, Short, Byte, and Clojure collections. This ensures that the hash code
returned is consistent with `=`.
### 2.17 Java 7 Documentation
`*core-java-api*` will now return the URL for the Java 7 Javadoc when you are
running Java 7.
### 2.18 loadLibrary Loads Library Using System ClassLoader
A static method, `loadLibrary`, was added to `clojure.lang.RT` to load a
library using the system ClassLoader instead of Clojure's class loader.
### 2.19 Java int is Boxed As java.lang.Integer
Java `int`s are now boxed as `java.lang.Integer`s. See
[the discussion on clojure-dev](https://groups.google.com/forum/#!msg/clojure/7-hARL5c1lI/ntnnOweEGfUJ)
for more information.
## 3 Performance Enhancements
* `(= char char)` is now optimized
* `equiv` is inlined in variadic =
* `toString` cached on keywords and symbols
## 4 Bug Fixes
* [CLJ-829](http://dev.clojure.org/jira/browse/CLJ-829)
Transient hashmaps mishandle hash collisions
* [CLJ-773](http://dev.clojure.org/jira/browse/CLJ-773)
Macros that are expanded away still have their vars referenced in the emitted byte code
* [CLJ-837](http://dev.clojure.org/jira/browse/CLJ-837)
java.lang.VerifyError when compiling deftype or defrecord with argument name starting with double underscore characters
* [CLJ-369](http://dev.clojure.org/jira/browse/CLJ-369)
Check for invalid interface method names
* [CLJ-845](http://dev.clojure.org/jira/browse/CLJ-845)
Unexpected interaction between protocol extension and namespaced method keyword/symbols
* Ignoring namespace portion of symbols used to name methods in extend-type and extend-protocol
* [CLJ-852](http://dev.clojure.org/jira/browse/CLJ-852)
IllegalArgumentException thrown when defining a var whose value is calculated with a primitive fn
* [CLJ-855](http://dev.clojure.org/jira/browse/CLJ-855)
catch receives a RuntimeException rather than the expected checked exception
* [CLJ-876](http://dev.clojure.org/jira/browse/CLJ-876)
#^:dynamic vars declared in a nested form are not immediately dynamic
* [CLJ-886](http://dev.clojure.org/jira/browse/CLJ-886)
java.io/do-copy can garble multibyte characters
* [CLJ-895](http://dev.clojure.org/jira/browse/CLJ-895)
Collection.toArray implementations do not conform to Java API docs
* obey contract for toArray return type
* [CLJ-898](http://dev.clojure.org/jira/browse/CLJ-898)
Agent sends consume heap
* Only capture a shallow copy of the current Frame in binding-conveyor-fn, so that sends in agent actions don't build infinite Frame stacks
* [CLJ-928](http://dev.clojure.org/jira/browse/CLJ-928)
Instant literal for Date and Timestamp should print in UTC
* [CLJ-931](http://dev.clojure.org/jira/browse/CLJ-933)
Syntactically broken clojure.test/are tests succeed
* [CLJ-933](http://dev.clojure.org/jira/browse/CLJ-933)
Compiler warning on clojure.test-clojure.require-scratch
# Changes to Clojure in Version 1.3
## CONTENTS
1 Deprecated and Removed Features
1.1 Earmuffed Vars are No Longer Automatically Considered Dynamic
1.2 ISeq No Longer Inherits from Sequential
1.3 Removed Bit Operation Support for Boxed Numbers
1.4 Ancillary Namespaces No Longer Auto-Load on Startup
1.5 Replicate Deprecated
2 New/Improved Features
2.1 Enhanced Primitive Support
2.2 defrecord and deftype Improvements
2.3 Better Exception Reporting
2.4 clojure.reflect/reflect
2.5 clojure.data/diff
2.6 clojure.core/every-pred and clojure.core/some-fn Combinators
2.7 clojure.core/realized?
2.8 clojure.core/with-redefs-fn & with-redefs
2.9 clojure.core/find-keyword
2.10 clojure.repl/pst
2.11 clojure.pprint/print-table
2.12 pprint respects *print-length*
2.13 compilation and deployment via Maven
2.14 internal keyword map uses weak refs
2.15 ^:const defs
2.16 Message Bearing Assert
2.17 Error Checking for defmulti Options
2.18 Removed Checked Exceptions
2.19 vector-of Takes Multiple Arguments
2.20 deref with timeout
2.21 Walk Support for sorted-by Collections
2.22 string.join Enhanced to Work with Sets
2.23 clojure.test-helper
2.24 Newline outputs platform-specific newline sequence
2.25 init-proxy and update-proxy return proxy
2.26 doc & find-doc moved to REPL
2.27 clojure.java.shell/sh accepts as input anything that clojure.java.io/copy does
2.28 InterruptedHandler Promoted to clojure.repl
2.29 Add support for running -main namespaces from clojure.main
2.30 Set thread names on agent thread pools
2.31 Add docstring support to def
2.32 Comp function returns identity when called with zero arity
2.33 Type hints can be applied to arg vectors
2.34 Binding Conveyance
3 Performance Enhancements
4 Bug Fixes
5 Modular Contrib
## 1 Deprecated and Removed Features
### 1.1 Earmuffed Vars Are No Longer Automatically Considered Dynamic.
(def *fred*)
=> Warning: *fred* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic ** or change the name.
### 1.2 ISeq No Longer Inherits From Sequential
This allows ISeq implementers to be in the map or set equality partition.
### 1.3 Removed Bit Operation Support for Boxed Numbers
Bit Operations map directly to primitive operations
### 1.4 Ancillary Namespaces No Longer Auto-Load on Startup
The following namespaces are no longer loaded on startup: clojure.set, clojure.xml, clojure.zip
### 1.5 Replicate Deprecated
Use repeat instead.
## 2 New/Improved Features
### 2.1 Enhanced Primitive Support
Full details here:
- [Enhanced Primitive Support][EPS]
- [Documentation for 1.3 Numerics][NUM]
[EPS]: http://dev.clojure.org/display/doc/Enhanced+Primitive+Support
[NUM]: http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics
### 2.2 defrecord and deftype Improvements
Details here: [Defrecord Improvements](http://dev.clojure.org/display/design/defrecord+improvements)
### 2.3 Better Exception Reporting
Details here: [Error Handling](http://dev.clojure.org/display/design/Error+Handling)
Additionally:
Better error messages:
* When calling macros with arity
* For Invalid Map Literals
* For alias function if using unknown namespace
* In the REPL
* Add "starting at " to EOF while reading exceptions
* Better compilation error reporting
### 2.4 clojure.reflect/reflect
Full details here: [Reflection API](http://dev.clojure.org/display/design/Reflection+API)
### 2.5 clojure.data/diff
Recursively compares a and b, returning a tuple of [things-only-in-a things-only-in-b things-in-both]
(diff {:a 1 :b 2} {:a 1 :b 22 :c 3})
=> ({:b 2} {:c 3, :b 22} {:a 1})
### 2.6 clojure.core/every-pred and clojure.core/some-fn Combinators
every-pred takes a set of predicates and returns a function f that returns true if all of its composing predicates return a logical true value against all of its arguments, else it returns false.
((every-pred even?) 2 4 6)
=> true
((every-pred even?) 2 4 5)
=>false
some-fn takes a set of predicates and returns a function f that returns the first logical true value returned by one of its composing predicates against any of its arguments, else it returns logical false.
((some-fn even?) 2 4 5)
=> true
((some-fn odd?) 2 4 6)
=> false
### 2.7 clojure.core/realized?
Returns true if a value has been produced for a promise, delay, future or lazy sequence.
(let [x (range 5)]
(println (realized? x))
(first x)
(println (realized? x)))
=> false
=> true
### 2.8 clojure.core/with-redefs-fn & clojure.core/with-redefs
with-redefs-fn temporarily redefines Vars during a call to func. with-redefs temporarily redefines Vars while executing the body.
(with-redefs [nil? :temp] (println nil?))
=> :temp
### 2.9 clojure.core/find-keyword
Returns a Keyword with the given namespace and name if one already exists.
(find-keyword "def")
=> :def
(find-keyword "fred")
=> nil
### 2.10 clojure.repl/pst
Prints a stack trace of the exception
(pst (IllegalArgumentException.))
IllegalArgumentException
user/eval27 (NO_SOURCE_FILE:18)
clojure.lang.Compiler.eval (Compiler.java:6355)
clojure.lang.Compiler.eval (Compiler.java:6322)
clojure.core/eval (core.clj:2699)
clojure.main/repl/read-eval-print--5906 (main.clj:244)
clojure.main/repl/fn--5911 (main.clj:265)
clojure.main/repl (main.clj:265)
clojure.main/repl-opt (main.clj:331)
clojure.main/main (main.clj:427)
clojure.lang.Var.invoke (Var.java:397)
clojure.lang.Var.applyTo (Var.java:518)
clojure.main.main (main.java:37)
### 2.11 clojure.pprint/print-table
Prints a collection of maps in a textual table.
(print-table [:fred :barney]
[{:fred "ethel"}
{:fred "wilma" :barney "betty"}])
===============
:fred | :barney
===============
ethel |
wilma | betty
===============
### 2.12 pprint respects \*print-length\*
Assigning \*print-length\* now affects output of pprint
### 2.13 compilation and deployment via Maven
See the following pages for more information:
- [Maven Settings and Repositories][MSR]
- [Why Maven?][WM]
- [Common Contrib Build][CCB]
- [How to Make Releases][HMR]
[MSR]: http://dev.clojure.org/display/doc/Maven+Settings+and+Repositories
[WM]: http://dev.clojure.org/pages/viewpage.action?pageId=950842
[CCB]: http://dev.clojure.org/display/design/Common+Contrib+Build
[HMR]:http://dev.clojure.org/display/design/How+to+Make+Releases
### 2.14 internal keyword map uses weak refs
### 2.15 ^:const defs
^:const lets you name primitive values with speedier reference.
(def constants
{:pi 3.14
:e 2.71})
(def ^:const pi (:pi constants))
(def ^:const e (:e constants))
The overhead of looking up :e and :pi in the map happens at compile time, as (:pi constants) and (:e constants) are evaluated when their parent def forms are evaluated.
### 2.16 Message Bearing Assert
Assert can take a second argument which will be printed when the assert fails
(assert (= 1 2) "1 is not equal to 2")
=> AssertionError Assert failed: 1 is not equal to 2
### 2.17 Error Checking for defmulti Options
defmulti will check to verify that its options are valid. For example, the following code will throw an exception:
(defmulti fred :ethel :lucy :ricky)
=> IllegalArgumentException
### 2.18 Removed Checked Exceptions
Clojure does not throw checked exceptions
### 2.19 vector-of Takes Multiple Args
vector-of takes multiple args used to populate the array
(vector-of :int 1 2 3)
=> [1 2 3]
### 2.20 deref with timeout
deref now takes a timeout option - when given with a blocking reference, will return the timeout-val if the timeout (in milliseconds) is reached before value is available.
(deref (promise) 10 :ethel)
=> :ethel
### 2.21 Walk Support for sorted-by Collections
Walk modified to work on sorted-by collections
let [x (sorted-set-by > 1 2 3)] (walk inc reverse x))
=> (2 3 4)
### 2.22 string.join Enhanced to Work with Sets
Just like join works on other collections
(join " and " #{:fred :ethel :lucy})
=> ":lucy and :fred and :ethel"
### 2.23 clojure.test-helper
All test helpers moved into clojure.test-helper
### 2.24 Newline outputs platform-specific newline sequence
Newline sequence is output as \r\n on Windows now.
### 2.25 init-proxy and update-proxy return proxy
Now you can chain calls on the proxy
### 2.26 doc & find-doc moved to REPL
Adds special form docs to the REPL
### 2.27 clojure.java.shell/sh accepts as input anything that clojure.java.io/copy does
This adds InputStream, Reader, File, byte[] to the list of inputs for clojure.java.shell/sh
### 2.28 Interrupt Handler Promoted to clojure.repl
Promoting this library eliminates the need for a dependency on old contrib.
### 2.29 Add support for running -main namespaces from clojure.main
This patch allows clojure.main to accept an argument pointing to a namespace to look for a -main function in. This allows users to write -main functions that will work the same whether the code is AOT-compiled for use in an executable jar or just run from source.
### 2.30 Set thread names on agent thread pools
It's a best practice to name the threads in an executor thread pool with a custom ThreadFactory so that the purpose of these threads is clear in thread dumps and other runtime operational tools.
Patch causes thread names like:
clojure-agent-send-pool-%d (should be fixed # of threads)
clojure-agent-send-off-pool-%d (will be added and removed over time)
### 2.31 Add docstring support to def
A def can now have a docstring between name and value.
(def foo "a foo" :foo)
### 2.32 Comp function returns identity when called with zero arity
(= (comp) identity)
=> true
### 2.33 Type hints can be applied to arg vectors
You can hint different arities separately:
(defn hinted
(^String [])
(^Integer [a])
(^java.util.List [a & args]))
This is preferred over hinting the function name. Hinting the function name is still allowed for backward compatibility, but will likely be deprecated in a future release.
### 2.34 Binding Conveyance
Clojure APIs that pass work off to other threads (e.g. send, send-off, pmap, future) now convey the dynamic bindings of the calling thread:
(def ^:dynamic *num* 1)
(binding [*num* 2] (future (println *num*)))
;; prints "2", not "1"
## 3 Performance Enhancements
* Code path for using vars is now much faster for the common case
* Improved startup time
* Fix performance on some numeric overloads
See [CLJ-380](http://dev.clojure.org/jira/browse/CLJ-5) for more information
* Promises are lock free
* Functions only get metadata support code when metadata explicitly supplied
* definterface/gen-interface accepts array type hints
* inline nil?
* inline bit-functions & math ops
* inline n-ary min & max
* PersistentQueue count is now O(1)
* Intrinsics: unchecked math operators now emit bytecodes directly where possible
## 4 Bug Fixes
[Complete list of Tickets for 1.3 Release][ISSUES].
[ISSUES]: http://dev.clojure.org/jira/secure/IssueNavigator.jspa?mode=hide&requestId=10052
* [CLJ-8](http://dev.clojure.org/jira/browse/CLJ-8)
detect and report cyclic load dependencies
* Patch restore detection of cyclic load dependencies
* [CLJ-31](http://dev.clojure.org/jira/browse/CLJ-31)
compiler now correctly rejects attempts to recur across try
(fn [x] (try (recur 1)))
=> CompilerException
* [CLJ-286](http://dev.clojure.org/jira/browse/CLJ-286)
\*out\* being used as java.io.PrintWriter
* Patch fixes using Writer instead of PrintWriter
* fix clojure.main to not assume that *err* is a PrintWriter
* [CLJ-292](http://dev.clojure.org/jira/browse/CLJ-292)
LazySeq.sval() nests RuntimeExceptions
* Patch causes only the original RuntimeException to be thrown
* [CLJ-390](http://dev.clojure.org/jira/browse/CLJ-390)
sends from agent error-handlers should be allowed
* Patch allows agent error-handler to send successfully
* [CLJ-426](http://dev.clojure.org/jira/browse/CLJ-426)
case should handle hash collision
* There were situations where a hash collision would occur with case and an exception would be thrown. See [discussion](https://groups.google.com/d/topic/clojure/m4ZDWKSfmfo/discussion) for more details
* [CLJ-430](http://dev.clojure.org/jira/browse/CLJ-430)
clojure.java.io URL Coercion throws java.lang.ClassCastException
* Patch correct exception to be thrown
* [CLJ-432](http://dev.clojure.org/jira/browse/CLJ-432)
deftype does not work if containing ns contains dashes
* Patch munges namespaces with dashes properly
* [CLJ-433](http://dev.clojure.org/jira/browse/CLJ-433)
munge should not munge $ (which isJavaIdentifierPart), should munge ' (which is not)
* [CLJ-435](http://dev.clojure.org/jira/browse/CLJ-435)
stackoverflow exception in printing meta with :type
* Patch fixes exception being thrown on certain type metadata
(with-meta {:value 2} {:type Object})
=> No message. [Thrown class java.lang.StackOverflowError]
* [CLJ-437](http://dev.clojure.org/jira/browse/CLJ-437)
Bugs in clojure.set/subset? and superset? for sets with false/nil elements
* Patch fixes failing on subset? and superset? for sets with false/nil elements
* [CLJ-439](http://dev.clojure.org/jira/browse/CLJ-439)
Automatic type translation from Integer to Long
* Patch fixes increase coercion from Integer to Long
* [CLJ-444](http://dev.clojure.org/jira/browse/CLJ-444)
Infinite recursion in Keyword.intern leads to stack overflow
* No more infinite recursion with patch
* [CLJ-673](http://dev.clojure.org/jira/browse/CLJ-673)
use system class loader when base loader is null
* facilitates placing Clojure on bootclasspath
* [CLJ-678](http://dev.clojure.org/jira/browse/CLJ-678)
into-array should work with all primitive types
* [CLJ-680](http://dev.clojure.org/jira/browse/CLJ-680)
printing promises should not block
* Patch allows printing of promises without blocking
* [CLJ-682](http://dev.clojure.org/jira/browse/CLJ-682)
cl-format: ~w throws an exception when not wrapped in a pretty-writer
* Patch fixes the following bug in cl-format with ~w:
* [CLJ-693](http://dev.clojure.org/jira/browse/CLJ-693)
VerifyError with symbol metadata, macros, and defrecord
* [CLJ-702](http://dev.clojure.org/jira/browse/CLJ-702)
case gives NPE when used with nil
* Patch allows nil to be used with case
* [CLJ-734](http://dev.clojure.org/jira/browse/CLJ-734)
starting scope of let bindings seems incorrect from jdi perspective
* Patch fixes local variables table to have the correct code index for let bindings.
* [CLJ-739](http://dev.clojure.org/jira/browse/CLJ-739)
version.properties file is not closed
* Patch properly closes version.properties file
* [CLJ-751](http://dev.clojure.org/jira/browse/CLJ-751)
cl-format: ~( throws an exception with an empty string
* Patch fixes the following bug in cl-format when format is nil
(cl-format nil "~:(~a~)" "")
=> NullPointerException
* [CLJ-780](http://dev.clojure.org/jira/browse/CLJ-780)
race condition in reference cache on Java 5
* Map.Entry instances can have null values prior to Java 6. This patch provides a workaround.
* floats were being boxed as Doubles, now they are boxed as Floats
* several "holding onto head" fixes
* Stop top-level defs from hanging onto the head of an expression that uses a lazy seq
* Stop multimethods from holding onto heads of their arguments
## 5 Modular Contrib
In 1.3, the monolithic clojure-contrib.jar has been replaced by a modular system of contrib libraries, so that production systems can include only the code they actually need. This also allows individual contribs to have their own release cycles. Many contribs have moved forward by several point versions already. Documentation for updating applications to use the new contrib libraries is at http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go
Important Note: Many of the new modular contribs are compatible with both 1.2 and 1.3. This offers an incremental migration path: First, upgrade your contrib libraries while holding Clojure at 1.2, Then, in a separate step, upgrade to Clojure 1.3.
================================================
FILE: clojure.iml
================================================
================================================
FILE: doc/clojure/pprint/CommonLispFormat.markdown
================================================
# A Common Lisp-compatible Format Function
cl-format is an implementation of the incredibly baroque Common Lisp format function as specified
in [Common Lisp, the Language, 2nd edition, Chapter 22](http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000).
Format gives you an easy and powerful way to format text and data for output. It supports rich
formatting of strings and numbers, loops, conditionals, embedded formats, etc. It is really a
domain-specific language for formatting.
This implementation for clojure has the following goals:
* Support the full feature set of the Common Lisp format function (including the X3J13 extensions) with the only exception being concepts that make no sense or are differently interpreted in Clojure.
* Make porting code from Common Lisp easier.
* Provide a more native feeling solution for Clojure programmers than the Java format method and its relatives.
* Be fast. This includes the ability to precompile formats that are going to be used repetitively.
* Include useful error handling and comprehensive documentation.
## Why would I use cl-format?
For some people the answer to this question is that they are used to
Common Lisp and, therefore, they already know the syntax of format
strings and all the directives.
A more interesting answer is that cl-format provides a way of
rendering strings that is much more suited to Lisp and its data
structures.
Because iteration and conditionals are built into the directive
structure of cl-format, it is possible to render sequences and other
complex data structures directly without having to loop over the data
structure.
For example, to print the elements of a sequence separated by commas,
you simply say:
(cl-format true "~{~a~^, ~}" aseq)
(This example is taken from
[Practical Common Lisp](http://www.gigamonkeys.com/book/)
by Peter Seibel.)
The corresponding output using Clojure's Java-based _format_ function
would involve a nasty loop/recur with some code to figure out about
the commas. Yuck!
## Current Status of cl-format
cl-format is 100% compatible with the Common Lisp standard as
specified in CLtLv2.
This includes all of the functionality of Common
Lisp's format function including iteration, conditionals,
text justification and rich
options for displaying real and integer values. It also includes the
directives to support pretty printing structured output.
If you find a bug in a directive, drop me a line
with a chunk of code that exhibits the bug and the version of
cl-format you found it in and I'll try to get it fixed.
I also intend to have good built-in documentation for the directives,
but I haven't built that yet.
The following directives are
not yet supported: ~:T and ~@:T (but all other forms of ~T work)
and extensions with ~/.
The pretty printer interface is similar, but not identical to the
interface in Common Lisp.
Next up:
* Support for ~/
* True compiled formats
* Restructure unit tests into modular chunks.
* Import tests from CLISP and SBCL.
* Unit tests for exception conditions.
* Interactive documentation
## How to use cl-format
### Loading cl-format in your program
Once cl-format is in your path, adding it to your code is easy:
(ns your-namespace-here
(:use [clojure.pprint :only (cl-format)]))
If you want to refer to the cl-format function as "format" (rather
than using the clojure function of that name), you can use this idiom:
(ns your-namespace-here
(:refer-clojure :exclude [format])
(:use clojure.pprint))
(def format cl-format)
You might want to do this in code that you've ported from Common Lisp,
for instance, or maybe just because old habits die hard.
From the REPL, you can grab it using (use):
(use 'clojure.pprint)
### Calling cl-format
cl-format is a standard clojure function that takes a variable number
of arguments. You call it like this:
(cl-format stream format args...)
_stream_ can be any Java Writer (that is java.io.Writer) or the values
_true_, _false_, or _nil_. The argument _true_ is identical to using
`*`out`*` while _false_ or _nil_ indicate that cl-format should return
its result as a string rather than writing it to a stream.
_format_ is either a format string or a compiled format (see
below). The format string controls the output that's written in a way
that's similar to (but much more powerful than) the standard Clojure
API format function (which is based on Java's
java.lang.String.Format).
Format strings consist of characters that are to be written to the
output stream plus directives (which are marked by ~) as in "The
answer is ~,2f". Format strings are documented in detail in
[*Common Lisp the Language*, 2nd edition, Chapter 22](http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000).
_args_ is a set of arguments whose use is defined by the format.
## Using column aware streams across format invocations
Writers in Java have no real idea of current column or device page width, so the format
directives that want to work relative to the current position on the
page have nothing to work with. To deal with this, cl-format contains
an extension to writer called pretty-writer. A pretty-writer watches the
output and keeps track of what column the current output is going to.
When you call format and your format includes a directive that cares
about what column it's in (~T, ~&, ~<...~>), cl-format will
automatically wrap the Writer you passed in with a pretty-writer. This
means that by default all cl-format statements act like they begin on
a fresh line and have a page width of 72.
For many applications, these assumptions are fine and you need to do
nothing more. But sometimes you want to use multiple cl-format calls
that output partial lines. You may also want to mix cl-format calls
with the native clojure calls like print. If you want stay
column-aware while doing this you need to create a pretty-writer of
your own (and possibly bind it to `*`out`*`).
As an example of this, this function takes a nested list and prints it
as a table (returning the result as a string):
(defn list-to-table [aseq column-width]
(let [string-writer (java.io.StringWriter.)
stream (get-pretty-writer string-writer)]
(binding [*out* stream]
(doseq [row aseq]
(doseq [col row]
(cl-format true "~4D~7,vT" col column-width))
(prn)))
(.flush stream)
(.toString string-writer)))
(In reality, you'd probably do this as a single call to cl-format.)
The get-pretty-writer function takes the Writer to wrap and
(optionally) the page width (in columns) for use with ~<...~>.
## Examples
The following function uses cl-format to dump a columnized table of the Java system properties:
(defn show-props [stream]
(let [p (mapcat
#(vector (key %) (val %))
(sort-by key (System/getProperties)))]
(cl-format stream "~30A~A~%~{~20,,,'-A~10A~}~%~{~30A~S~%~}"
"Property" "Value" ["" "" "" ""] p)))
There are some more examples in the pretty print examples gallery at
http://github.com/tomfaulhaber/pprint-examples:
* hexdump - a program that uses cl-format to create a standard formatted hexdump of the requested stream.
* multiply - a function to show a formatted multiplication table in a very "first-order" way.
* props - the show-props example shown above.
* show_doc - some utilities for showing documentation from various name spaces.
## Differences from the Common Lisp format function
The floating point directives that show exponents (~E, ~G) show E for
the exponent character in all cases (unless overridden with an
_exponentchar_). Clojure does not distinguish between floats and
doubles in its printed representation and neither does cl-format.
The ~A and ~S directives accept the colon prefix, but ignore it since
() and nil are not equivalent in Clojure.
Clojure has 3 different reader syntaxes for characters. The ~@c
directive to cl-format has an argument extension to let you choose:
* ~@c (with no argument) prints "\c" (backslash followed by the printed representation of the character or \newline, \space, \tab, \backspace, \return)
* ~'o@c prints "\oDDD" where DDD are the octal digits representing the character.
* ~'u@c prints "\uXXXX" prints the hex Unicode representation of the character.
================================================
FILE: doc/clojure/pprint/PrettyPrinting.markdown
================================================
# A Pretty Printer for Clojure
## Overview
This namespace adds a new feature to Clojure: a generalized pretty
printer.
The pretty printer is easy to use:
user=> (println (for [x (range 10)] (range x)))
(() (0) (0 1) (0 1 2) (0 1 2 3) (0 1 2 3 4) (0 1 2 3 4 5) (0 1 2 3 4 5 6) (0 1 2 3 4 5 6 7) (0 1 2 3 4 5 6 7 8))
nil
user=> (use 'clojure.pprint)
nil
user=> (pprint (for [x (range 10)] (range x)))
(()
(0)
(0 1)
(0 1 2)
(0 1 2 3)
(0 1 2 3 4)
(0 1 2 3 4 5)
(0 1 2 3 4 5 6)
(0 1 2 3 4 5 6 7)
(0 1 2 3 4 5 6 7 8))
nil
user=>
The pretty printer supports two modes: _code_ which has special
formatting for special forms and core macros and _simple_ (the
default) which formats the various Clojure data structures as
appropriate for raw data. In fact, the pretty printer is
highly customizable, but basic use is pretty simple.
All the functions and variables described here are in the
clojure.pprint namespace. Using them is as simple as adding a
`(:use clojure.pprint)` to
your namespace declarations. Or, better practice would be
`(:use [clojure.pprint :only ()])`.
pprint is being developed by Tom Faulhaber (to mail me you can use
my first name at my domain which is infolace.com).
As with the rest of Clojure, the pretty printer is licensed under the
[http://opensource.org/licenses/eclipse-1.0.php Eclipse Public License 1.0].
Future development is guided by those using it, so send feedback about
what's working and not working for you and what you'd like to see in the
pretty printer.
## Pretty Printing Basics
Pretty printing is primarily implemented with the function
pprint. pprint takes a single argument and formats it according to the
settings of several special variables.
Generally, the defaults are fine for pretty printing and you can
simply use:
(pprint obj)
to print your object. If you wish to write to
another stream besides `*`out`*`, you can use:
(write obj :pretty true :stream foo)
where foo is the stream to which you wish to write. (The write
function has a lot more options which are not yet documented. Stay
tuned.)
When at the REPL, the pp macro pretty prints the last output
value. This is useful when you get something too complex to read
comfortably. Just type:
user=> (pp)
and you'll get a pretty printed version of the last thing output (the
magic variable `*`1).
## Dispatch tables and code formatting
The behavior of the pretty printer can be finely controlled through
the use of _dispatch tables_ that contain descriptions for how
different structures should be formatted.
Using custom dispatch tables, the pretty printer can create formatted
output for data structures that is customized for the
application. This allows pretty printing to be baked into any
structured output. For information and examples, see below in
[#Custom_Dispatch_Functions Custom Dispatch Functions].
The pretty printer comes with two pre-defined dispatch tables to cover
the most common situations:
`*`simple-dispatch`*` - supports basic representation of data in various
Clojure structures: seqs, maps, vectors, etc. in a fairly standard
way. When structures need to be broken across lines, following lines
are indented to line up with the first element. `*`simple-dispatch`*` is
the default and is good for showing the output of most operations.
`*`code-dispatch`*` - has special representation for various structures
found in code: defn, condp, binding vectors, anonymous functions,
etc. This dispatch indents following lines of a list one more space as
appropriate for a function/argument type of list.
An example formatted with code dispatch:
user=> (def code '(defn cl-format
"An implementation of a Common Lisp compatible format function"
[stream format-in & args] (let [compiled-format (if (string? format-in)
(compile-format format-in) format-in) navigator (init-navigator args)]
(execute-format stream compiled-format navigator))))
#'user/code
user=> (with-pprint-dispatch *code-dispatch* (pprint code))
(defn cl-format
"An implementation of a Common Lisp compatible format function"
[stream format-in & args]
(let [compiled-format (if (string? format-in)
(compile-format format-in)
format-in)
navigator (init-navigator args)]
(execute-format stream compiled-format navigator)))
nil
user=>
There are three ways to set the current dispatch: set it to a specific
table permanently with set-pprint-dispatch, bind it with
with-pprint-dispatch (as shown in the example above), or use the
:dispatch keyword argument to write.
## Control variables
The operation of pretty printing is also controlled by a set of variables
that control general parameters of how the pretty printer makes
decisions. The current list is as follows:
*`*`print-pretty`*`*: Default: *true*
Bind to true if you want write to use pretty printing. (pprint and pp automatically
bind this to true.)
*`*`print-right-margin`*`*: Default: *72*
Pretty printing will try to avoid anything going beyond this column.
*`*`print-miser-width`*`*: Default: *40*
The column at which to enter miser style. Depending on the dispatch table,
miser style add newlines in more places to try to keep lines short allowing for further
levels of nesting. For example, in the code dispatch table, the pretty printer will
insert a newline between the "if" and its condition when in miser style.
*`*`print-suppress-namespaces`*`*: Default: *false*
Don't print namespaces with symbols. This is particularly useful when
pretty printing the results of macro expansions
*`*`print-level`*`*: Default: *nil*
As with the regular Clojure print function, this variable controls the
depth of structure that is printed. The argument itself is level 0,
the first level of a collection is level 1, etc. When the structure
gets deeper than the specified `*`print-level`*`, a hash sign (#) is
printed.
For example:
user=> (binding [*print-level* 2] (pprint '(a b (c d) ((e) ((f d) g)))))
(a b (c d) (# #))
nil
user=>
*`*`print-length`*`*: Default: *nil*
As with the regular Clojure print function, this variable controls the
number of items that are printed at each layer of structure. When a
layer has too many items, ellipses (...) are displayed.
For example:
user=> (defn foo [x] (for [i (range x) ] (range 1 (- x (dec i)))))
#'user/foo
user=> (binding [*print-length* 6] (pprint (foo 10)))
((1 2 3 4 5 6 ...)
(1 2 3 4 5 6 ...)
(1 2 3 4 5 6 ...)
(1 2 3 4 5 6 ...)
(1 2 3 4 5 6)
(1 2 3 4 5)
...)
nil
user=>
## Custom Dispatch Functions
Using custom dispatch, you can easily create your own formatted output
for structured data. Examples included with the pretty printer show
how to use custom dispatch to translate simple Clojure structures into
nicely formatted JSON and XML.
### Basic Concepts of Pretty Printing
In order to create custom dispatch functions, you need to understand
the fundamentals of pretty printing. The clojure pretty printer is
based on the XP pretty printer algorithm (used in many Lisps including
Common Lisp) which supports sophisticated decision-making about line
breaking and indentation with reasonable performance even for very
large structures. The XP algorithm is documented in the paper,
[http://dspace.mit.edu/handle/1721.1/6504 XP. A Common Lisp Pretty
Printing System].
The Clojure implementation of XP is similar in spirit to the Common
Lisp implementation, but the details of the interface are somewhat
different. The result is that writing custom dispatch in Clojure is
more "Clojure-y."
There are three key concepts to understand when creating custom pretty
printing functions: _logical blocks_, _conditional newlines_, and
_indentation_.
A _logical block_ marks a set of output that should be thought about
as a single unit by the pretty printer. Logical blocks can contain
other logical blocks (that is, they nest). As a simple example, when
printing list structure, every sublist will typically be a logical
block.
_Conditional newlines_ tell the pretty printer where it can insert
line breaks and how to make the decisions about when to do it. There
are four types of conditional newline:
* Linear newlines tell the pretty printer to insert a newline in a
place whenever the enclosing logical block won't fit on a single
line. Linear newlines are an all-or-nothing proposition; if the
logical block doesn't fit on a single line, *all* the linear
newlines are emitted as actual newlines.
* Fill newlines tell the pretty printer that it should fit as many
chunks of the logical block as possible on this line and then emit
a newline.
* Mandatory newlines tell the pretty printer to emit a newline
regardless of where it is in the output line.
* Miser newlines tell the pretty printer to emit a newline if the
output column is in the miser region (as defined by the pretty
printer variable `*`pprint-miser-width`*`). This allows you to
define special behavior as the output gets heavily nested near the
right margin.
_Indentation_ commands allow you to specify how wrapped lines should
be indented. Indentation can be relative to either the start column of
the current logical block or the current column position of the output.
(This section is still incomplete...)
## Current limitations and future plans
This is an early version release of the pretty printer and there is
plenty that is yet to come.
Here are some examples:
* Support all the types and forms in Clojure (most of the way there now).
* Support for limiting pretty printing based on line counts.
* Support for circular and shared substructure detection.
* Finishing the integration with the format function (support for ~/ and tabular pretty printing).
* Performance! (Not much thought has been made to making this go fast, but there are a bunch of pretty obvious speedups to be had.)
* Handle Java objects intelligently
Please let me know about anything that's not working right, anything that
should work differently, or the feature you think should be at the top
of my list.
================================================
FILE: epl-v10.html
================================================
Eclipse Public License - Version 1.0
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program
originate from and are distributed by that particular Contributor. A
Contribution 'originates' from a Contributor if it was added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf. Contributions do not include additions to the
Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii)
are not derivative works of the Program.
"Contributor" means any person or entity that distributes
the Program.
"Licensed Patents" mean patent claims licensable by a
Contributor which are necessarily infringed by the use or sale of its
Contribution alone or when combined with the Program.
"Program" means the Contributions distributed in accordance
with this Agreement.
"Recipient" means anyone who receives the Program under
this Agreement, including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free copyright license to reproduce, prepare derivative works
of, publicly display, publicly perform, distribute and sublicense the
Contribution of such Contributor, if any, and such derivative works, in
source code and object code form.
b) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell,
offer to sell, import and otherwise transfer the Contribution of such
Contributor, if any, in source code and object code form. This patent
license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor,
such addition of the Contribution causes such combination to be covered
by the Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor
grants the licenses to its Contributions set forth herein, no assurances
are provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.
d) Each Contributor represents that to its knowledge it
has sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:
a) it complies with the terms and conditions of this
Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors
all warranties and conditions, express and implied, including warranties
or conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and
iv) states that source code for the Program is available
from such Contributor, and informs licensees how to obtain it in a
reasonable manner on or through a medium customarily used for software
exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each
copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use of
the Program, the Contributor who includes the Program in a commercial
product offering should do so in a manner which does not create
potential liability for other Contributors. Therefore, if a Contributor
includes the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and
indemnify every other Contributor ("Indemnified Contributor")
against any losses, damages and costs (collectively "Losses")
arising from claims, lawsuits and other legal actions brought by a third
party against the Indemnified Contributor to the extent caused by the
acts or omissions of such Commercial Contributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In
order to qualify, an Indemnified Contributor must: a) promptly notify
the Commercial Contributor in writing of such claim, and b) allow the
Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors, compliance with applicable laws,
damage to or loss of data, programs or equipment, and unavailability or
interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), 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 OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other
software or hardware) infringes such Recipient's patent(s), then such
Recipient's rights granted under Section 2(b) shall terminate as of the
date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted by
Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this
Agreement, but in order to avoid inconsistency the Agreement is
copyrighted and may only be modified in the following manner. The
Agreement Steward reserves the right to publish new versions (including
revisions) of this Agreement from time to time. No one other than the
Agreement Steward has the right to modify this Agreement. The Eclipse
Foundation is the initial Agreement Steward. The Eclipse Foundation may
assign the responsibility to serve as the Agreement Steward to a
suitable separate entity. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version
of the Agreement is published, Contributor may elect to distribute the
Program (including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor under
this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.
This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No party
to this Agreement will bring a legal action under this Agreement more
than one year after the cause of action arose. Each party waives its
rights to a jury trial in any resulting litigation.
The constant pool from the original class is copied as is in the new
* class, which saves time. New constant pool entries will be added at the
* end if necessary, but unused constant pool entries won't be
* removed.
*
Methods that are not transformed are copied as is in the new class,
* directly from the original class bytecode (i.e. without emitting visit
* events for all the method instructions), which saves a lot of
* time. Untransformed methods are detected by the fact that the
* {@link ClassReader} receives {@link MethodVisitor} objects that come from
* a {@link ClassWriter} (and not from any other {@link ClassVisitor}
* instance).
*
*
* @param classReader
* the {@link ClassReader} used to read the original class. It
* will be used to copy the entire constant pool from the
* original class and also to copy other fragments of original
* bytecode where applicable.
* @param flags
* option flags that can be used to modify the default behavior
* of this class. These option flags do not affect methods
* that are copied as is in the new class. This means that the
* maximum stack size nor the stack frames will be computed for
* these methods. See {@link #COMPUTE_MAXS},
* {@link #COMPUTE_FRAMES}.
*/
public ClassWriter(final ClassReader classReader, final int flags) {
this(flags);
classReader.copyPool(this);
this.cr = classReader;
}
// ------------------------------------------------------------------------
// Implementation of the ClassVisitor abstract class
// ------------------------------------------------------------------------
@Override
public final void visit(final int version, final int access,
final String name, final String signature, final String superName,
final String[] interfaces) {
this.version = version;
this.access = access;
this.name = newClass(name);
thisName = name;
if (ClassReader.SIGNATURES && signature != null) {
this.signature = newUTF8(signature);
}
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]);
}
}
}
@Override
public final void visitSource(final String file, final String debug) {
if (file != null) {
sourceFile = newUTF8(file);
}
if (debug != null) {
sourceDebug = new ByteVector().putUTF8(debug);
}
}
@Override
public final void visitOuterClass(final String owner, final String name,
final String desc) {
enclosingMethodOwner = newClass(owner);
if (name != null && desc != null) {
enclosingMethod = newNameType(name, desc);
}
}
@Override
public final AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector();
// write type, and reserve space for values count
bv.putShort(newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2);
if (visible) {
aw.next = anns;
anns = aw;
} else {
aw.next = ianns;
ianns = aw;
}
return aw;
}
@Override
public final void visitAttribute(final Attribute attr) {
attr.next = attrs;
attrs = attr;
}
@Override
public final void visitInnerClass(final String name,
final String outerName, final String innerName, final int access) {
if (innerClasses == null) {
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);
}
@Override
public final FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
return new FieldWriter(this, access, name, desc, signature, value);
}
@Override
public final MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
return new MethodWriter(this, access, name, desc, signature,
exceptions, computeMaxs, computeFrames);
}
@Override
public final 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() {
if (index > 0xFFFF) {
throw new RuntimeException("Class file too large!");
}
// computes the real size of the bytecode of this class
int size = 24 + 2 * interfaceCount;
int nbFields = 0;
FieldWriter fb = firstField;
while (fb != null) {
++nbFields;
size += fb.getSize();
fb = (FieldWriter) fb.fv;
}
int nbMethods = 0;
MethodWriter mb = firstMethod;
while (mb != null) {
++nbMethods;
size += mb.getSize();
mb = (MethodWriter) mb.mv;
}
int attributeCount = 0;
if (bootstrapMethods != null) {
// we put it as first attribute in order to improve a bit
// ClassReader.copyBootstrapMethods
++attributeCount;
size += 8 + bootstrapMethods.length;
newUTF8("BootstrapMethods");
}
if (ClassReader.SIGNATURES && signature != 0) {
++attributeCount;
size += 8;
newUTF8("Signature");
}
if (sourceFile != 0) {
++attributeCount;
size += 8;
newUTF8("SourceFile");
}
if (sourceDebug != null) {
++attributeCount;
size += sourceDebug.length + 4;
newUTF8("SourceDebugExtension");
}
if (enclosingMethodOwner != 0) {
++attributeCount;
size += 10;
newUTF8("EnclosingMethod");
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
++attributeCount;
size += 6;
newUTF8("Deprecated");
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((version & 0xFFFF) < Opcodes.V1_5
|| (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) {
++attributeCount;
size += 6;
newUTF8("Synthetic");
}
}
if (innerClasses != null) {
++attributeCount;
size += 8 + innerClasses.length;
newUTF8("InnerClasses");
}
if (ClassReader.ANNOTATIONS && anns != null) {
++attributeCount;
size += 8 + anns.getSize();
newUTF8("RuntimeVisibleAnnotations");
}
if (ClassReader.ANNOTATIONS && ianns != null) {
++attributeCount;
size += 8 + ianns.getSize();
newUTF8("RuntimeInvisibleAnnotations");
}
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);
int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE
| ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC);
out.putShort(access & ~mask).putShort(name).putShort(superName);
out.putShort(interfaceCount);
for (int i = 0; i < interfaceCount; ++i) {
out.putShort(interfaces[i]);
}
out.putShort(nbFields);
fb = firstField;
while (fb != null) {
fb.put(out);
fb = (FieldWriter) fb.fv;
}
out.putShort(nbMethods);
mb = firstMethod;
while (mb != null) {
mb.put(out);
mb = (MethodWriter) mb.mv;
}
out.putShort(attributeCount);
if (bootstrapMethods != null) {
out.putShort(newUTF8("BootstrapMethods"));
out.putInt(bootstrapMethods.length + 2).putShort(
bootstrapMethodsCount);
out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length);
}
if (ClassReader.SIGNATURES && signature != 0) {
out.putShort(newUTF8("Signature")).putInt(2).putShort(signature);
}
if (sourceFile != 0) {
out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile);
}
if (sourceDebug != null) {
int len = sourceDebug.length - 2;
out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
out.putByteArray(sourceDebug.data, 2, len);
}
if (enclosingMethodOwner != 0) {
out.putShort(newUTF8("EnclosingMethod")).putInt(4);
out.putShort(enclosingMethodOwner).putShort(enclosingMethod);
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
out.putShort(newUTF8("Deprecated")).putInt(0);
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((version & 0xFFFF) < Opcodes.V1_5
|| (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) {
out.putShort(newUTF8("Synthetic")).putInt(0);
}
}
if (innerClasses != null) {
out.putShort(newUTF8("InnerClasses"));
out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
out.putByteArray(innerClasses.data, 0, innerClasses.length);
}
if (ClassReader.ANNOTATIONS && anns != null) {
out.putShort(newUTF8("RuntimeVisibleAnnotations"));
anns.put(out);
}
if (ClassReader.ANNOTATIONS && ianns != null) {
out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
if (attrs != null) {
attrs.put(this, null, 0, -1, -1, out);
}
if (invalidFrames) {
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES);
return cw.toByteArray();
}
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 Integer}, a {@link Float}, a
* {@link Long}, a {@link Double}, a {@link String} or a
* {@link Type}.
* @return a new or already existing constant item with the given value.
*/
Item newConstItem(final Object cst) {
if (cst instanceof Integer) {
int val = ((Integer) cst).intValue();
return newInteger(val);
} else if (cst instanceof Byte) {
int val = ((Byte) cst).intValue();
return newInteger(val);
} else if (cst instanceof Character) {
int val = ((Character) cst).charValue();
return newInteger(val);
} else if (cst instanceof Short) {
int val = ((Short) cst).intValue();
return newInteger(val);
} else if (cst instanceof Boolean) {
int val = ((Boolean) cst).booleanValue() ? 1 : 0;
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 if (cst instanceof Type) {
Type t = (Type) cst;
int s = t.getSort();
if (s == Type.OBJECT) {
return newClassItem(t.getInternalName());
} else if (s == Type.METHOD) {
return newMethodTypeItem(t.getDescriptor());
} else { // s == primitive type or array
return newClassItem(t.getDescriptor());
}
} else if (cst instanceof Handle) {
Handle h = (Handle) cst;
return newHandleItem(h.tag, h.owner, h.name, h.desc);
} 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 Integer}, a {@link Float}, a
* {@link Long}, a {@link Double} or a {@link String}.
* @return the index of a new or already existing constant item with the
* given value.
*/
public int newConst(final Object cst) {
return newConstItem(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 a new or already existing class reference item.
*/
Item newClassItem(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;
}
/**
* 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) {
return newClassItem(value).index;
}
/**
* Adds a method type 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 methodDesc
* method descriptor of the method type.
* @return a new or already existing method type reference item.
*/
Item newMethodTypeItem(final String methodDesc) {
key2.set(MTYPE, methodDesc, null, null);
Item result = get(key2);
if (result == null) {
pool.put12(MTYPE, newUTF8(methodDesc));
result = new Item(index++, key2);
put(result);
}
return result;
}
/**
* Adds a method type 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 methodDesc
* method descriptor of the method type.
* @return the index of a new or already existing method type reference
* item.
*/
public int newMethodType(final String methodDesc) {
return newMethodTypeItem(methodDesc).index;
}
/**
* Adds a handle 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 tag
* the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
* {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
* {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the field or method owner class.
* @param name
* the name of the field or method.
* @param desc
* the descriptor of the field or method.
* @return a new or an already existing method type reference item.
*/
Item newHandleItem(final int tag, final String owner, final String name,
final String desc) {
key4.set(HANDLE_BASE + tag, owner, name, desc);
Item result = get(key4);
if (result == null) {
if (tag <= Opcodes.H_PUTSTATIC) {
put112(HANDLE, tag, newField(owner, name, desc));
} else {
put112(HANDLE,
tag,
newMethod(owner, name, desc,
tag == Opcodes.H_INVOKEINTERFACE));
}
result = new Item(index++, key4);
put(result);
}
return result;
}
/**
* Adds a handle 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 tag
* the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
* {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
* {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the field or method owner class.
* @param name
* the name of the field or method.
* @param desc
* the descriptor of the field or method.
* @return the index of a new or already existing method type reference
* item.
*/
public int newHandle(final int tag, final String owner, final String name,
final String desc) {
return newHandleItem(tag, owner, name, desc).index;
}
/**
* Adds an invokedynamic 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 name
* name of the invoked method.
* @param desc
* descriptor of the invoke method.
* @param bsm
* the bootstrap method.
* @param bsmArgs
* the bootstrap method constant arguments.
*
* @return a new or an already existing invokedynamic type reference item.
*/
Item newInvokeDynamicItem(final String name, final String desc,
final Handle bsm, final Object... bsmArgs) {
// cache for performance
ByteVector bootstrapMethods = this.bootstrapMethods;
if (bootstrapMethods == null) {
bootstrapMethods = this.bootstrapMethods = new ByteVector();
}
int position = bootstrapMethods.length; // record current position
int hashCode = bsm.hashCode();
bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name,
bsm.desc));
int argsLength = bsmArgs.length;
bootstrapMethods.putShort(argsLength);
for (int i = 0; i < argsLength; i++) {
Object bsmArg = bsmArgs[i];
hashCode ^= bsmArg.hashCode();
bootstrapMethods.putShort(newConst(bsmArg));
}
byte[] data = bootstrapMethods.data;
int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments)
hashCode &= 0x7FFFFFFF;
Item result = items[hashCode % items.length];
loop: while (result != null) {
if (result.type != BSM || result.hashCode != hashCode) {
result = result.next;
continue;
}
// because the data encode the size of the argument
// we don't need to test if these size are equals
int resultPosition = result.intVal;
for (int p = 0; p < length; p++) {
if (data[position + p] != data[resultPosition + p]) {
result = result.next;
continue loop;
}
}
break;
}
int bootstrapMethodIndex;
if (result != null) {
bootstrapMethodIndex = result.index;
bootstrapMethods.length = position; // revert to old position
} else {
bootstrapMethodIndex = bootstrapMethodsCount++;
result = new Item(bootstrapMethodIndex);
result.set(position, hashCode);
put(result);
}
// now, create the InvokeDynamic constant
key3.set(name, desc, bootstrapMethodIndex);
result = get(key3);
if (result == null) {
put122(INDY, bootstrapMethodIndex, newNameType(name, desc));
result = new Item(index++, key3);
put(result);
}
return result;
}
/**
* Adds an invokedynamic 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 name
* name of the invoked method.
* @param desc
* descriptor of the invoke method.
* @param bsm
* the bootstrap method.
* @param bsmArgs
* the bootstrap method constant arguments.
*
* @return the index of a new or already existing invokedynamic reference
* item.
*/
public int newInvokeDynamic(final String name, final String desc,
final Handle bsm, final Object... bsmArgs) {
return newInvokeDynamicItem(name, desc, bsm, bsmArgs).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.
*
* @param owner
* the internal name of the field's owner class.
* @param name
* the field's name.
* @param desc
* the field's descriptor.
* @return a new or already existing field reference item.
*/
Item newFieldItem(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;
}
/**
* 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) {
return newFieldItem(owner, name, desc).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) {
int type = itf ? IMETH : METH;
key3.set(type, owner, name, desc);
Item result = get(key3);
if (result == null) {
put122(type, 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.
*/
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.
*/
Item newFloat(final float value) {
key.set(value);
Item result = get(key);
if (result == null) {
pool.putByte(FLOAT).putInt(key.intVal);
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.
*/
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);
index += 2;
put(result);
}
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.
*/
Item newDouble(final double value) {
key.set(value);
Item result = get(key);
if (result == null) {
pool.putByte(DOUBLE).putLong(key.longVal);
result = new Item(index, key);
index += 2;
put(result);
}
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. This
* method is intended for {@link Attribute} sub classes, and is normally not
* needed by class generators or adapters.
*
* @param name
* a name.
* @param desc
* a type descriptor.
* @return the index of a new or already existing name and type item.
*/
public int newNameType(final String name, final String desc) {
return newNameTypeItem(name, desc).index;
}
/**
* 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 a new or already existing name and type item.
*/
Item newNameTypeItem(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;
}
/**
* Adds the given internal name to {@link #typeTable} and returns its index.
* Does nothing if the type table already contains this internal name.
*
* @param type
* the internal name to be added to the type table.
* @return the index of this internal name in the type table.
*/
int addType(final String type) {
key.set(TYPE_NORMAL, type, null, null);
Item result = get(key);
if (result == null) {
result = addType(key);
}
return result.index;
}
/**
* Adds the given "uninitialized" type to {@link #typeTable} and returns its
* index. This method is used for UNINITIALIZED types, made of an internal
* name and a bytecode offset.
*
* @param type
* the internal name to be added to the type table.
* @param offset
* the bytecode offset of the NEW instruction that created this
* UNINITIALIZED type value.
* @return the index of this internal name in the type table.
*/
int addUninitializedType(final String type, final int offset) {
key.type = TYPE_UNINIT;
key.intVal = offset;
key.strVal1 = type;
key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset);
Item result = get(key);
if (result == null) {
result = addType(key);
}
return result.index;
}
/**
* Adds the given Item to {@link #typeTable}.
*
* @param item
* the value to be added to the type table.
* @return the added Item, which a new Item instance with the same value as
* the given Item.
*/
private Item addType(final Item item) {
++typeCount;
Item result = new Item(typeCount, key);
put(result);
if (typeTable == null) {
typeTable = new Item[16];
}
if (typeCount == typeTable.length) {
Item[] newTable = new Item[2 * typeTable.length];
System.arraycopy(typeTable, 0, newTable, 0, typeTable.length);
typeTable = newTable;
}
typeTable[typeCount] = result;
return result;
}
/**
* Returns the index of the common super type of the two given types. This
* method calls {@link #getCommonSuperClass} and caches the result in the
* {@link #items} hash table to speedup future calls with the same
* parameters.
*
* @param type1
* index of an internal name in {@link #typeTable}.
* @param type2
* index of an internal name in {@link #typeTable}.
* @return the index of the common super type of the two given types.
*/
int getMergedType(final int type1, final int type2) {
key2.type = TYPE_MERGED;
key2.longVal = type1 | (((long) type2) << 32);
key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2);
Item result = get(key2);
if (result == null) {
String t = typeTable[type1].strVal1;
String u = typeTable[type2].strVal1;
key2.intVal = addType(getCommonSuperClass(t, u));
result = new Item((short) 0, key2);
put(result);
}
return result.intVal;
}
/**
* Returns the common super type of the two given types. The default
* implementation of this method loads the two given classes and uses
* the java.lang.Class methods to find the common super class. It can be
* overridden to compute this common super type in other ways, in particular
* without actually loading any class, or to take into account the class
* that is currently being generated by this ClassWriter, which can of
* course not be loaded since it is under construction.
*
* @param type1
* the internal name of a class.
* @param type2
* the internal name of another class.
* @return the internal name of the common super class of the two given
* classes.
*/
protected String getCommonSuperClass(final String type1, final String type2) {
Class> c, d;
ClassLoader classLoader = getClass().getClassLoader();
try {
c = Class.forName(type1.replace('/', '.'), false, classLoader);
d = Class.forName(type2.replace('/', '.'), false, classLoader);
} catch (Exception e) {
throw new RuntimeException(e.toString());
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
/**
* 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) {
Item i = items[key.hashCode % items.length];
while (i != null && (i.type != key.type || !key.isEqualTo(i))) {
i = i.next;
}
return i;
}
/**
* 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 + typeCount > threshold) {
int ll = items.length;
int nl = ll * 2 + 1;
Item[] newItems = new Item[nl];
for (int l = ll - 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) (nl * 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);
}
/**
* Puts two bytes and one short into the constant pool.
*
* @param b1
* a byte.
* @param b2
* another byte.
* @param s
* a short.
*/
private void put112(final int b1, final int b2, final int s) {
pool.put11(b1, b2).putShort(s);
}
public SourceWriter getSc() {
return sc;
}
}
================================================
FILE: src/jvm/clojure/asm/Context.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* Information about a class being parsed in a {@link ClassReader}.
*
* @author Eric Bruneton
*/
class Context {
/**
* Prototypes of the attributes that must be parsed for this class.
*/
Attribute[] attrs;
/**
* The {@link ClassReader} option flags for the parsing of this class.
*/
int flags;
/**
* The buffer used to read strings.
*/
char[] buffer;
/**
* The start index of each bootstrap method.
*/
int[] bootstrapMethods;
/**
* The access flags of the method currently being parsed.
*/
int access;
/**
* The name of the method currently being parsed.
*/
String name;
/**
* The descriptor of the method currently being parsed.
*/
String desc;
/**
* The offset of the latest stack map frame that has been parsed.
*/
int offset;
/**
* The encoding of the latest stack map frame that has been parsed.
*/
int mode;
/**
* The number of locals in the latest stack map frame that has been parsed.
*/
int localCount;
/**
* The number locals in the latest stack map frame that has been parsed,
* minus the number of locals in the previous frame.
*/
int localDiff;
/**
* The local values of the latest stack map frame that has been parsed.
*/
Object[] local;
/**
* The stack size of the latest stack map frame that has been parsed.
*/
int stackCount;
/**
* The stack values of the latest stack map frame that has been parsed.
*/
Object[] stack;
}
================================================
FILE: src/jvm/clojure/asm/Edge.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* An edge in the control flow graph of a method body. See {@link Label Label}.
*
* @author Eric Bruneton
*/
class Edge {
/**
* Denotes a normal control flow graph edge.
*/
static final int NORMAL = 0;
/**
* Denotes a control flow graph edge corresponding to an exception handler.
* More precisely any {@link Edge} whose {@link #info} is strictly positive
* corresponds to an exception handler. The actual value of {@link #info} is
* the index, in the {@link ClassWriter} type table, of the exception that
* is catched.
*/
static final int EXCEPTION = 0x7FFFFFFF;
/**
* Information about this control flow graph edge. If
* {@link ClassWriter#COMPUTE_MAXS} is used this field is 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. If {@link ClassWriter#COMPUTE_FRAMES} is used,
* this field is the kind of this control flow graph edge (i.e. NORMAL or
* EXCEPTION).
*/
int info;
/**
* 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;
}
================================================
FILE: src/jvm/clojure/asm/FieldVisitor.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( visitAnnotation | visitAttribute )*
* visitEnd.
*
* @author Eric Bruneton
*/
public abstract class FieldVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
* must be one of {@link Opcodes#ASM4}.
*/
protected final int api;
/**
* The field visitor to which this visitor must delegate method calls. May
* be null.
*/
protected FieldVisitor fv;
/**
* Constructs a new {@link FieldVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
*/
public FieldVisitor(final int api) {
this(api, null);
}
/**
* Constructs a new {@link FieldVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param fv
* the field visitor to which this visitor must delegate method
* calls. May be null.
*/
public FieldVisitor(final int api, final FieldVisitor fv) {
if (api != Opcodes.ASM4) {
throw new IllegalArgumentException();
}
this.api = api;
this.fv = fv;
}
/**
* Visits an annotation of the field.
*
* @param desc
* the class descriptor of the annotation class.
* @param visible
* true if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or null if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (fv != null) {
return fv.visitAnnotation(desc, visible);
}
return null;
}
/**
* Visits a non standard attribute of the field.
*
* @param attr
* an attribute.
*/
public void visitAttribute(Attribute attr) {
if (fv != null) {
fv.visitAttribute(attr);
}
}
/**
* Visits the end of the field. This method, which is the last one to be
* called, is used to inform the visitor that all the annotations and
* attributes of the field have been visited.
*/
public void visitEnd() {
if (fv != null) {
fv.visitEnd();
}
}
}
================================================
FILE: src/jvm/clojure/asm/FieldWriter.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* An {@link FieldVisitor} that generates Java fields in bytecode form.
*
* @author Eric Bruneton
*/
final class FieldWriter extends FieldVisitor {
/**
* The class writer to which this field must be added.
*/
private final ClassWriter cw;
/**
* Access flags of this field.
*/
private final int access;
/**
* The index of the constant pool item that contains the name of this
* method.
*/
private final int name;
/**
* The index of the constant pool item that contains the descriptor of this
* field.
*/
private final int desc;
/**
* The index of the constant pool item that contains the signature of this
* field.
*/
private int signature;
/**
* The index of the constant pool item that contains the constant value of
* this field.
*/
private int value;
/**
* The runtime visible annotations of this field. May be null.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this field. May be null.
*/
private AnnotationWriter ianns;
/**
* The non standard attributes of this field. May be null.
*/
private Attribute attrs;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new {@link FieldWriter}.
*
* @param cw
* the class writer to which this field must be added.
* @param access
* the field's access flags (see {@link Opcodes}).
* @param name
* the field's name.
* @param desc
* the field's descriptor (see {@link Type}).
* @param signature
* the field's signature. May be null.
* @param value
* the field's constant value. May be null.
*/
FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) {
super(Opcodes.ASM4);
if (cw.firstField == null) {
cw.firstField = this;
} else {
cw.lastField.fv = this;
}
cw.lastField = this;
this.cw = cw;
this.access = access;
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
if (ClassReader.SIGNATURES && signature != null) {
this.signature = cw.newUTF8(signature);
}
if (value != null) {
this.value = cw.newConstItem(value).index;
}
}
// ------------------------------------------------------------------------
// Implementation of the FieldVisitor abstract class
// ------------------------------------------------------------------------
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector();
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
if (visible) {
aw.next = anns;
anns = aw;
} else {
aw.next = ianns;
ianns = aw;
}
return aw;
}
@Override
public void visitAttribute(final Attribute attr) {
attr.next = attrs;
attrs = attr;
}
@Override
public void visitEnd() {
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Returns the size of this field.
*
* @return the size of this field.
*/
int getSize() {
int size = 8;
if (value != 0) {
cw.newUTF8("ConstantValue");
size += 8;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
cw.newUTF8("Synthetic");
size += 6;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
cw.newUTF8("Deprecated");
size += 6;
}
if (ClassReader.SIGNATURES && signature != 0) {
cw.newUTF8("Signature");
size += 8;
}
if (ClassReader.ANNOTATIONS && anns != null) {
cw.newUTF8("RuntimeVisibleAnnotations");
size += 8 + anns.getSize();
}
if (ClassReader.ANNOTATIONS && ianns != null) {
cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize();
}
if (attrs != null) {
size += attrs.getSize(cw, null, 0, -1, -1);
}
return size;
}
/**
* Puts the content of this field into the given byte vector.
*
* @param out
* where the content of this field must be put.
*/
void put(final ByteVector out) {
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
out.putShort(access & ~mask).putShort(name).putShort(desc);
int attributeCount = 0;
if (value != 0) {
++attributeCount;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
++attributeCount;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
++attributeCount;
}
if (ClassReader.SIGNATURES && signature != 0) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && anns != null) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && ianns != null) {
++attributeCount;
}
if (attrs != null) {
attributeCount += attrs.getCount();
}
out.putShort(attributeCount);
if (value != 0) {
out.putShort(cw.newUTF8("ConstantValue"));
out.putInt(2).putShort(value);
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
out.putShort(cw.newUTF8("Synthetic")).putInt(0);
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
}
if (ClassReader.SIGNATURES && signature != 0) {
out.putShort(cw.newUTF8("Signature"));
out.putInt(2).putShort(signature);
}
if (ClassReader.ANNOTATIONS && anns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
anns.put(out);
}
if (ClassReader.ANNOTATIONS && ianns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
if (attrs != null) {
attrs.put(cw, null, 0, -1, -1, out);
}
}
}
================================================
FILE: src/jvm/clojure/asm/Frame.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* Information about the input and output stack map frames of a basic block.
*
* @author Eric Bruneton
*/
final class Frame {
/*
* Frames are computed in a two steps process: during the visit of each
* instruction, the state of the frame at the end of current basic block is
* updated by simulating the action of the instruction on the previous state
* of this so called "output frame". In visitMaxs, a fix point algorithm is
* used to compute the "input frame" of each basic block, i.e. the stack map
* frame at the beginning of the basic block, starting from the input frame
* of the first basic block (which is computed from the method descriptor),
* and by using the previously computed output frames to compute the input
* state of the other blocks.
*
* All output and input frames are stored as arrays of integers. Reference
* and array types are represented by an index into a type table (which is
* not the same as the constant pool of the class, in order to avoid adding
* unnecessary constants in the pool - not all computed frames will end up
* being stored in the stack map table). This allows very fast type
* comparisons.
*
* Output stack map frames are computed relatively to the input frame of the
* basic block, which is not yet known when output frames are computed. It
* is therefore necessary to be able to represent abstract types such as
* "the type at position x in the input frame locals" or "the type at
* position x from the top of the input frame stack" or even "the type at
* position x in the input frame, with y more (or less) array dimensions".
* This explains the rather complicated type format used in output frames.
*
* This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a
* signed number of array dimensions (from -8 to 7). KIND is either BASE,
* LOCAL or STACK. BASE is used for types that are not relative to the input
* frame. LOCAL is used for types that are relative to the input local
* variable types. STACK is used for types that are relative to the input
* stack types. VALUE depends on KIND. For LOCAL types, it is an index in
* the input local variable types. For STACK types, it is a position
* relatively to the top of input frame stack. For BASE types, it is either
* one of the constants defined in FrameVisitor, or for OBJECT and
* UNINITIALIZED types, a tag and an index in the type table.
*
* Output frames can contain types of any kind and with a positive or
* negative dimension (and even unassigned types, represented by 0 - which
* does not correspond to any valid type value). Input frames can only
* contain BASE types of positive or null dimension. In all cases the type
* table contains only internal type names (array type descriptors are
* forbidden - dimensions must be represented through the DIM field).
*
* The LONG and DOUBLE types are always represented by using two slots (LONG
* + TOP or DOUBLE + TOP), for local variable types as well as in the
* operand stack. This is necessary to be able to simulate DUPx_y
* instructions, whose effect would be dependent on the actual type values
* if types were always represented by a single slot in the stack (and this
* is not possible, since actual type values are not always known - cf LOCAL
* and STACK type kinds).
*/
/**
* Mask to get the dimension of a frame type. This dimension is a signed
* integer between -8 and 7.
*/
static final int DIM = 0xF0000000;
/**
* Constant to be added to a type to get a type with one more dimension.
*/
static final int ARRAY_OF = 0x10000000;
/**
* Constant to be added to a type to get a type with one less dimension.
*/
static final int ELEMENT_OF = 0xF0000000;
/**
* Mask to get the kind of a frame type.
*
* @see #BASE
* @see #LOCAL
* @see #STACK
*/
static final int KIND = 0xF000000;
/**
* Flag used for LOCAL and STACK types. Indicates that if this type happens
* to be a long or double type (during the computations of input frames),
* then it must be set to TOP because the second word of this value has been
* reused to store other data in the basic block. Hence the first word no
* longer stores a valid long or double value.
*/
static final int TOP_IF_LONG_OR_DOUBLE = 0x800000;
/**
* Mask to get the value of a frame type.
*/
static final int VALUE = 0x7FFFFF;
/**
* Mask to get the kind of base types.
*/
static final int BASE_KIND = 0xFF00000;
/**
* Mask to get the value of base types.
*/
static final int BASE_VALUE = 0xFFFFF;
/**
* Kind of the types that are not relative to an input stack map frame.
*/
static final int BASE = 0x1000000;
/**
* Base kind of the base reference types. The BASE_VALUE of such types is an
* index into the type table.
*/
static final int OBJECT = BASE | 0x700000;
/**
* Base kind of the uninitialized base types. The BASE_VALUE of such types
* in an index into the type table (the Item at that index contains both an
* instruction offset and an internal class name).
*/
static final int UNINITIALIZED = BASE | 0x800000;
/**
* Kind of the types that are relative to the local variable types of an
* input stack map frame. The value of such types is a local variable index.
*/
private static final int LOCAL = 0x2000000;
/**
* Kind of the the types that are relative to the stack of an input stack
* map frame. The value of such types is a position relatively to the top of
* this stack.
*/
private static final int STACK = 0x3000000;
/**
* The TOP type. This is a BASE type.
*/
static final int TOP = BASE | 0;
/**
* The BOOLEAN type. This is a BASE type mainly used for array types.
*/
static final int BOOLEAN = BASE | 9;
/**
* The BYTE type. This is a BASE type mainly used for array types.
*/
static final int BYTE = BASE | 10;
/**
* The CHAR type. This is a BASE type mainly used for array types.
*/
static final int CHAR = BASE | 11;
/**
* The SHORT type. This is a BASE type mainly used for array types.
*/
static final int SHORT = BASE | 12;
/**
* The INTEGER type. This is a BASE type.
*/
static final int INTEGER = BASE | 1;
/**
* The FLOAT type. This is a BASE type.
*/
static final int FLOAT = BASE | 2;
/**
* The DOUBLE type. This is a BASE type.
*/
static final int DOUBLE = BASE | 3;
/**
* The LONG type. This is a BASE type.
*/
static final int LONG = BASE | 4;
/**
* The NULL type. This is a BASE type.
*/
static final int NULL = BASE | 5;
/**
* The UNINITIALIZED_THIS type. This is a BASE type.
*/
static final int UNINITIALIZED_THIS = BASE | 6;
/**
* 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.
*/
static final int[] SIZE;
/**
* Computes the stack size variation corresponding to each JVM instruction.
*/
static {
int i;
int[] b = new int[202];
String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD"
+ "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD"
+ "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED"
+ "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
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, //INVOKEDYNAMIC, // visitInvokeDynamicInsn
// 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();
}
/**
* The label (i.e. basic block) to which these input and output stack map
* frames correspond.
*/
Label owner;
/**
* The input stack map frame locals.
*/
int[] inputLocals;
/**
* The input stack map frame stack.
*/
int[] inputStack;
/**
* The output stack map frame locals.
*/
private int[] outputLocals;
/**
* The output stack map frame stack.
*/
private int[] outputStack;
/**
* Relative size of the output stack. The exact semantics of this field
* depends on the algorithm that is used.
*
* When only the maximum stack size is computed, this field is the size of
* the output stack relatively to the top of the input stack.
*
* When the stack map frames are completely computed, this field is the
* actual number of types in {@link #outputStack}.
*/
private int outputStackTop;
/**
* Number of types that are initialized in the basic block.
*
* @see #initializations
*/
private int initializationCount;
/**
* The types that are initialized in the basic block. A constructor
* invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace
* every occurence of this type in the local variables and in the
* operand stack. This cannot be done during the first phase of the
* algorithm since, during this phase, the local variables and the operand
* stack are not completely computed. It is therefore necessary to store the
* types on which constructors are invoked in the basic block, in order to
* do this replacement during the second phase of the algorithm, where the
* frames are fully computed. Note that this array can contain types that
* are relative to input locals or to the input stack (see below for the
* description of the algorithm).
*/
private int[] initializations;
/**
* Returns the output frame local variable type at the given index.
*
* @param local
* the index of the local that must be returned.
* @return the output frame local variable type at the given index.
*/
private int get(final int local) {
if (outputLocals == null || local >= outputLocals.length) {
// this local has never been assigned in this basic block,
// so it is still equal to its value in the input frame
return LOCAL | local;
} else {
int type = outputLocals[local];
if (type == 0) {
// this local has never been assigned in this basic block,
// so it is still equal to its value in the input frame
type = outputLocals[local] = LOCAL | local;
}
return type;
}
}
/**
* Sets the output frame local variable type at the given index.
*
* @param local
* the index of the local that must be set.
* @param type
* the value of the local that must be set.
*/
private void set(final int local, final int type) {
// creates and/or resizes the output local variables array if necessary
if (outputLocals == null) {
outputLocals = new int[10];
}
int n = outputLocals.length;
if (local >= n) {
int[] t = new int[Math.max(local + 1, 2 * n)];
System.arraycopy(outputLocals, 0, t, 0, n);
outputLocals = t;
}
// sets the local variable
outputLocals[local] = type;
}
/**
* Pushes a new type onto the output frame stack.
*
* @param type
* the type that must be pushed.
*/
private void push(final int type) {
// creates and/or resizes the output stack array if necessary
if (outputStack == null) {
outputStack = new int[10];
}
int n = outputStack.length;
if (outputStackTop >= n) {
int[] t = new int[Math.max(outputStackTop + 1, 2 * n)];
System.arraycopy(outputStack, 0, t, 0, n);
outputStack = t;
}
// pushes the type on the output stack
outputStack[outputStackTop++] = type;
// updates the maximun height reached by the output stack, if needed
int top = owner.inputStackTop + outputStackTop;
if (top > owner.outputStackMax) {
owner.outputStackMax = top;
}
}
/**
* Pushes a new type onto the output frame stack.
*
* @param cw
* the ClassWriter to which this label belongs.
* @param desc
* the descriptor of the type to be pushed. Can also be a method
* descriptor (in this case this method pushes its return type
* onto the output frame stack).
*/
private void push(final ClassWriter cw, final String desc) {
int type = type(cw, desc);
if (type != 0) {
push(type);
if (type == LONG || type == DOUBLE) {
push(TOP);
}
}
}
/**
* Returns the int encoding of the given type.
*
* @param cw
* the ClassWriter to which this label belongs.
* @param desc
* a type descriptor.
* @return the int encoding of the given type.
*/
private static int type(final ClassWriter cw, final String desc) {
String t;
int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
switch (desc.charAt(index)) {
case 'V':
return 0;
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
return INTEGER;
case 'F':
return FLOAT;
case 'J':
return LONG;
case 'D':
return DOUBLE;
case 'L':
// stores the internal name, not the descriptor!
t = desc.substring(index + 1, desc.length() - 1);
return OBJECT | cw.addType(t);
// case '[':
default:
// extracts the dimensions and the element type
int data;
int dims = index + 1;
while (desc.charAt(dims) == '[') {
++dims;
}
switch (desc.charAt(dims)) {
case 'Z':
data = BOOLEAN;
break;
case 'C':
data = CHAR;
break;
case 'B':
data = BYTE;
break;
case 'S':
data = SHORT;
break;
case 'I':
data = INTEGER;
break;
case 'F':
data = FLOAT;
break;
case 'J':
data = LONG;
break;
case 'D':
data = DOUBLE;
break;
// case 'L':
default:
// stores the internal name, not the descriptor
t = desc.substring(dims + 1, desc.length() - 1);
data = OBJECT | cw.addType(t);
}
return (dims - index) << 28 | data;
}
}
/**
* Pops a type from the output frame stack and returns its value.
*
* @return the type that has been popped from the output frame stack.
*/
private int pop() {
if (outputStackTop > 0) {
return outputStack[--outputStackTop];
} else {
// if the output frame stack is empty, pops from the input stack
return STACK | -(--owner.inputStackTop);
}
}
/**
* Pops the given number of types from the output frame stack.
*
* @param elements
* the number of types that must be popped.
*/
private void pop(final int elements) {
if (outputStackTop >= elements) {
outputStackTop -= elements;
} else {
// if the number of elements to be popped is greater than the number
// of elements in the output stack, clear it, and pops the remaining
// elements from the input stack.
owner.inputStackTop -= elements - outputStackTop;
outputStackTop = 0;
}
}
/**
* Pops a type from the output frame stack.
*
* @param desc
* the descriptor of the type to be popped. Can also be a method
* descriptor (in this case this method pops the types
* corresponding to the method arguments).
*/
private void pop(final String desc) {
char c = desc.charAt(0);
if (c == '(') {
pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1);
} else if (c == 'J' || c == 'D') {
pop(2);
} else {
pop(1);
}
}
/**
* Adds a new type to the list of types on which a constructor is invoked in
* the basic block.
*
* @param var
* a type on a which a constructor is invoked.
*/
private void init(final int var) {
// creates and/or resizes the initializations array if necessary
if (initializations == null) {
initializations = new int[2];
}
int n = initializations.length;
if (initializationCount >= n) {
int[] t = new int[Math.max(initializationCount + 1, 2 * n)];
System.arraycopy(initializations, 0, t, 0, n);
initializations = t;
}
// stores the type to be initialized
initializations[initializationCount++] = var;
}
/**
* Replaces the given type with the appropriate type if it is one of the
* types on which a constructor is invoked in the basic block.
*
* @param cw
* the ClassWriter to which this label belongs.
* @param t
* a type
* @return t or, if t is one of the types on which a constructor is invoked
* in the basic block, the type corresponding to this constructor.
*/
private int init(final ClassWriter cw, final int t) {
int s;
if (t == UNINITIALIZED_THIS) {
s = OBJECT | cw.addType(cw.thisName);
} else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) {
String type = cw.typeTable[t & BASE_VALUE].strVal1;
s = OBJECT | cw.addType(type);
} else {
return t;
}
for (int j = 0; j < initializationCount; ++j) {
int u = initializations[j];
int dim = u & DIM;
int kind = u & KIND;
if (kind == LOCAL) {
u = dim + inputLocals[u & VALUE];
} else if (kind == STACK) {
u = dim + inputStack[inputStack.length - (u & VALUE)];
}
if (t == u) {
return s;
}
}
return t;
}
/**
* Initializes the input frame of the first basic block from the method
* descriptor.
*
* @param cw
* the ClassWriter to which this label belongs.
* @param access
* the access flags of the method to which this label belongs.
* @param args
* the formal parameter types of this method.
* @param maxLocals
* the maximum number of local variables of this method.
*/
void initInputFrame(final ClassWriter cw, final int access,
final Type[] args, final int maxLocals) {
inputLocals = new int[maxLocals];
inputStack = new int[0];
int i = 0;
if ((access & Opcodes.ACC_STATIC) == 0) {
if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) {
inputLocals[i++] = OBJECT | cw.addType(cw.thisName);
} else {
inputLocals[i++] = UNINITIALIZED_THIS;
}
}
for (int j = 0; j < args.length; ++j) {
int t = type(cw, args[j].getDescriptor());
inputLocals[i++] = t;
if (t == LONG || t == DOUBLE) {
inputLocals[i++] = TOP;
}
}
while (i < maxLocals) {
inputLocals[i++] = TOP;
}
}
/**
* Simulates the action of the given instruction on the output stack frame.
*
* @param opcode
* the opcode of the instruction.
* @param arg
* the operand of the instruction, if any.
* @param cw
* the class writer to which this label belongs.
* @param item
* the operand of the instructions, if any.
*/
void execute(final int opcode, final int arg, final ClassWriter cw,
final Item item) {
int t1, t2, t3, t4;
switch (opcode) {
case Opcodes.NOP:
case Opcodes.INEG:
case Opcodes.LNEG:
case Opcodes.FNEG:
case Opcodes.DNEG:
case Opcodes.I2B:
case Opcodes.I2C:
case Opcodes.I2S:
case Opcodes.GOTO:
case Opcodes.RETURN:
break;
case Opcodes.ACONST_NULL:
push(NULL);
break;
case Opcodes.ICONST_M1:
case Opcodes.ICONST_0:
case Opcodes.ICONST_1:
case Opcodes.ICONST_2:
case Opcodes.ICONST_3:
case Opcodes.ICONST_4:
case Opcodes.ICONST_5:
case Opcodes.BIPUSH:
case Opcodes.SIPUSH:
case Opcodes.ILOAD:
push(INTEGER);
break;
case Opcodes.LCONST_0:
case Opcodes.LCONST_1:
case Opcodes.LLOAD:
push(LONG);
push(TOP);
break;
case Opcodes.FCONST_0:
case Opcodes.FCONST_1:
case Opcodes.FCONST_2:
case Opcodes.FLOAD:
push(FLOAT);
break;
case Opcodes.DCONST_0:
case Opcodes.DCONST_1:
case Opcodes.DLOAD:
push(DOUBLE);
push(TOP);
break;
case Opcodes.LDC:
switch (item.type) {
case ClassWriter.INT:
push(INTEGER);
break;
case ClassWriter.LONG:
push(LONG);
push(TOP);
break;
case ClassWriter.FLOAT:
push(FLOAT);
break;
case ClassWriter.DOUBLE:
push(DOUBLE);
push(TOP);
break;
case ClassWriter.CLASS:
push(OBJECT | cw.addType("java/lang/Class"));
break;
case ClassWriter.STR:
push(OBJECT | cw.addType("java/lang/String"));
break;
case ClassWriter.MTYPE:
push(OBJECT | cw.addType("java/lang/invoke/MethodType"));
break;
// case ClassWriter.HANDLE_BASE + [1..9]:
default:
push(OBJECT | cw.addType("java/lang/invoke/MethodHandle"));
}
break;
case Opcodes.ALOAD:
push(get(arg));
break;
case Opcodes.IALOAD:
case Opcodes.BALOAD:
case Opcodes.CALOAD:
case Opcodes.SALOAD:
pop(2);
push(INTEGER);
break;
case Opcodes.LALOAD:
case Opcodes.D2L:
pop(2);
push(LONG);
push(TOP);
break;
case Opcodes.FALOAD:
pop(2);
push(FLOAT);
break;
case Opcodes.DALOAD:
case Opcodes.L2D:
pop(2);
push(DOUBLE);
push(TOP);
break;
case Opcodes.AALOAD:
pop(1);
t1 = pop();
push(ELEMENT_OF + t1);
break;
case Opcodes.ISTORE:
case Opcodes.FSTORE:
case Opcodes.ASTORE:
t1 = pop();
set(arg, t1);
if (arg > 0) {
t2 = get(arg - 1);
// if t2 is of kind STACK or LOCAL we cannot know its size!
if (t2 == LONG || t2 == DOUBLE) {
set(arg - 1, TOP);
} else if ((t2 & KIND) != BASE) {
set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE);
}
}
break;
case Opcodes.LSTORE:
case Opcodes.DSTORE:
pop(1);
t1 = pop();
set(arg, t1);
set(arg + 1, TOP);
if (arg > 0) {
t2 = get(arg - 1);
// if t2 is of kind STACK or LOCAL we cannot know its size!
if (t2 == LONG || t2 == DOUBLE) {
set(arg - 1, TOP);
} else if ((t2 & KIND) != BASE) {
set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE);
}
}
break;
case Opcodes.IASTORE:
case Opcodes.BASTORE:
case Opcodes.CASTORE:
case Opcodes.SASTORE:
case Opcodes.FASTORE:
case Opcodes.AASTORE:
pop(3);
break;
case Opcodes.LASTORE:
case Opcodes.DASTORE:
pop(4);
break;
case Opcodes.POP:
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
case Opcodes.IRETURN:
case Opcodes.FRETURN:
case Opcodes.ARETURN:
case Opcodes.TABLESWITCH:
case Opcodes.LOOKUPSWITCH:
case Opcodes.ATHROW:
case Opcodes.MONITORENTER:
case Opcodes.MONITOREXIT:
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
pop(1);
break;
case Opcodes.POP2:
case Opcodes.IF_ICMPEQ:
case Opcodes.IF_ICMPNE:
case Opcodes.IF_ICMPLT:
case Opcodes.IF_ICMPGE:
case Opcodes.IF_ICMPGT:
case Opcodes.IF_ICMPLE:
case Opcodes.IF_ACMPEQ:
case Opcodes.IF_ACMPNE:
case Opcodes.LRETURN:
case Opcodes.DRETURN:
pop(2);
break;
case Opcodes.DUP:
t1 = pop();
push(t1);
push(t1);
break;
case Opcodes.DUP_X1:
t1 = pop();
t2 = pop();
push(t1);
push(t2);
push(t1);
break;
case Opcodes.DUP_X2:
t1 = pop();
t2 = pop();
t3 = pop();
push(t1);
push(t3);
push(t2);
push(t1);
break;
case Opcodes.DUP2:
t1 = pop();
t2 = pop();
push(t2);
push(t1);
push(t2);
push(t1);
break;
case Opcodes.DUP2_X1:
t1 = pop();
t2 = pop();
t3 = pop();
push(t2);
push(t1);
push(t3);
push(t2);
push(t1);
break;
case Opcodes.DUP2_X2:
t1 = pop();
t2 = pop();
t3 = pop();
t4 = pop();
push(t2);
push(t1);
push(t4);
push(t3);
push(t2);
push(t1);
break;
case Opcodes.SWAP:
t1 = pop();
t2 = pop();
push(t1);
push(t2);
break;
case Opcodes.IADD:
case Opcodes.ISUB:
case Opcodes.IMUL:
case Opcodes.IDIV:
case Opcodes.IREM:
case Opcodes.IAND:
case Opcodes.IOR:
case Opcodes.IXOR:
case Opcodes.ISHL:
case Opcodes.ISHR:
case Opcodes.IUSHR:
case Opcodes.L2I:
case Opcodes.D2I:
case Opcodes.FCMPL:
case Opcodes.FCMPG:
pop(2);
push(INTEGER);
break;
case Opcodes.LADD:
case Opcodes.LSUB:
case Opcodes.LMUL:
case Opcodes.LDIV:
case Opcodes.LREM:
case Opcodes.LAND:
case Opcodes.LOR:
case Opcodes.LXOR:
pop(4);
push(LONG);
push(TOP);
break;
case Opcodes.FADD:
case Opcodes.FSUB:
case Opcodes.FMUL:
case Opcodes.FDIV:
case Opcodes.FREM:
case Opcodes.L2F:
case Opcodes.D2F:
pop(2);
push(FLOAT);
break;
case Opcodes.DADD:
case Opcodes.DSUB:
case Opcodes.DMUL:
case Opcodes.DDIV:
case Opcodes.DREM:
pop(4);
push(DOUBLE);
push(TOP);
break;
case Opcodes.LSHL:
case Opcodes.LSHR:
case Opcodes.LUSHR:
pop(3);
push(LONG);
push(TOP);
break;
case Opcodes.IINC:
set(arg, INTEGER);
break;
case Opcodes.I2L:
case Opcodes.F2L:
pop(1);
push(LONG);
push(TOP);
break;
case Opcodes.I2F:
pop(1);
push(FLOAT);
break;
case Opcodes.I2D:
case Opcodes.F2D:
pop(1);
push(DOUBLE);
push(TOP);
break;
case Opcodes.F2I:
case Opcodes.ARRAYLENGTH:
case Opcodes.INSTANCEOF:
pop(1);
push(INTEGER);
break;
case Opcodes.LCMP:
case Opcodes.DCMPL:
case Opcodes.DCMPG:
pop(4);
push(INTEGER);
break;
case Opcodes.JSR:
case Opcodes.RET:
throw new RuntimeException(
"JSR/RET are not supported with computeFrames option");
case Opcodes.GETSTATIC:
push(cw, item.strVal3);
break;
case Opcodes.PUTSTATIC:
pop(item.strVal3);
break;
case Opcodes.GETFIELD:
pop(1);
push(cw, item.strVal3);
break;
case Opcodes.PUTFIELD:
pop(item.strVal3);
pop();
break;
case Opcodes.INVOKEVIRTUAL:
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEINTERFACE:
pop(item.strVal3);
if (opcode != Opcodes.INVOKESTATIC) {
t1 = pop();
if (opcode == Opcodes.INVOKESPECIAL
&& item.strVal2.charAt(0) == '<') {
init(t1);
}
}
push(cw, item.strVal3);
break;
case Opcodes.INVOKEDYNAMIC:
pop(item.strVal2);
push(cw, item.strVal2);
break;
case Opcodes.NEW:
push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg));
break;
case Opcodes.NEWARRAY:
pop();
switch (arg) {
case Opcodes.T_BOOLEAN:
push(ARRAY_OF | BOOLEAN);
break;
case Opcodes.T_CHAR:
push(ARRAY_OF | CHAR);
break;
case Opcodes.T_BYTE:
push(ARRAY_OF | BYTE);
break;
case Opcodes.T_SHORT:
push(ARRAY_OF | SHORT);
break;
case Opcodes.T_INT:
push(ARRAY_OF | INTEGER);
break;
case Opcodes.T_FLOAT:
push(ARRAY_OF | FLOAT);
break;
case Opcodes.T_DOUBLE:
push(ARRAY_OF | DOUBLE);
break;
// case Opcodes.T_LONG:
default:
push(ARRAY_OF | LONG);
break;
}
break;
case Opcodes.ANEWARRAY:
String s = item.strVal1;
pop();
if (s.charAt(0) == '[') {
push(cw, '[' + s);
} else {
push(ARRAY_OF | OBJECT | cw.addType(s));
}
break;
case Opcodes.CHECKCAST:
s = item.strVal1;
pop();
if (s.charAt(0) == '[') {
push(cw, s);
} else {
push(OBJECT | cw.addType(s));
}
break;
// case Opcodes.MULTIANEWARRAY:
default:
pop(arg);
push(cw, item.strVal1);
break;
}
}
/**
* Merges the input frame of the given basic block with the input and output
* frames of this basic block. Returns true if the input frame of
* the given label has been changed by this operation.
*
* @param cw
* the ClassWriter to which this label belongs.
* @param frame
* the basic block whose input frame must be updated.
* @param edge
* the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}.
* @return true if the input frame of the given label has been
* changed by this operation.
*/
boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
boolean changed = false;
int i, s, dim, kind, t;
int nLocal = inputLocals.length;
int nStack = inputStack.length;
if (frame.inputLocals == null) {
frame.inputLocals = new int[nLocal];
changed = true;
}
for (i = 0; i < nLocal; ++i) {
if (outputLocals != null && i < outputLocals.length) {
s = outputLocals[i];
if (s == 0) {
t = inputLocals[i];
} else {
dim = s & DIM;
kind = s & KIND;
if (kind == BASE) {
t = s;
} else {
if (kind == LOCAL) {
t = dim + inputLocals[s & VALUE];
} else {
t = dim + inputStack[nStack - (s & VALUE)];
}
if ((s & TOP_IF_LONG_OR_DOUBLE) != 0
&& (t == LONG || t == DOUBLE)) {
t = TOP;
}
}
}
} else {
t = inputLocals[i];
}
if (initializations != null) {
t = init(cw, t);
}
changed |= merge(cw, t, frame.inputLocals, i);
}
if (edge > 0) {
for (i = 0; i < nLocal; ++i) {
t = inputLocals[i];
changed |= merge(cw, t, frame.inputLocals, i);
}
if (frame.inputStack == null) {
frame.inputStack = new int[1];
changed = true;
}
changed |= merge(cw, edge, frame.inputStack, 0);
return changed;
}
int nInputStack = inputStack.length + owner.inputStackTop;
if (frame.inputStack == null) {
frame.inputStack = new int[nInputStack + outputStackTop];
changed = true;
}
for (i = 0; i < nInputStack; ++i) {
t = inputStack[i];
if (initializations != null) {
t = init(cw, t);
}
changed |= merge(cw, t, frame.inputStack, i);
}
for (i = 0; i < outputStackTop; ++i) {
s = outputStack[i];
dim = s & DIM;
kind = s & KIND;
if (kind == BASE) {
t = s;
} else {
if (kind == LOCAL) {
t = dim + inputLocals[s & VALUE];
} else {
t = dim + inputStack[nStack - (s & VALUE)];
}
if ((s & TOP_IF_LONG_OR_DOUBLE) != 0
&& (t == LONG || t == DOUBLE)) {
t = TOP;
}
}
if (initializations != null) {
t = init(cw, t);
}
changed |= merge(cw, t, frame.inputStack, nInputStack + i);
}
return changed;
}
/**
* Merges the type at the given index in the given type array with the given
* type. Returns true if the type array has been modified by this
* operation.
*
* @param cw
* the ClassWriter to which this label belongs.
* @param t
* the type with which the type array element must be merged.
* @param types
* an array of types.
* @param index
* the index of the type that must be merged in 'types'.
* @return true if the type array has been modified by this
* operation.
*/
private static boolean merge(final ClassWriter cw, int t,
final int[] types, final int index) {
int u = types[index];
if (u == t) {
// if the types are equal, merge(u,t)=u, so there is no change
return false;
}
if ((t & ~DIM) == NULL) {
if (u == NULL) {
return false;
}
t = NULL;
}
if (u == 0) {
// if types[index] has never been assigned, merge(u,t)=t
types[index] = t;
return true;
}
int v;
if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) {
// if u is a reference type of any dimension
if (t == NULL) {
// if t is the NULL type, merge(u,t)=u, so there is no change
return false;
} else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) {
if ((u & BASE_KIND) == OBJECT) {
// if t is also a reference type, and if u and t have the
// same dimension merge(u,t) = dim(t) | common parent of the
// element types of u and t
v = (t & DIM) | OBJECT
| cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE);
} else {
// if u and t are array types, but not with the same element
// type, merge(u,t)=java/lang/Object
v = OBJECT | cw.addType("java/lang/Object");
}
} else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) {
// if t is any other reference or array type,
// merge(u,t)=java/lang/Object
v = OBJECT | cw.addType("java/lang/Object");
} else {
// if t is any other type, merge(u,t)=TOP
v = TOP;
}
} else if (u == NULL) {
// if u is the NULL type, merge(u,t)=t,
// or TOP if t is not a reference type
v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP;
} else {
// if u is any other type, merge(u,t)=TOP whatever t
v = TOP;
}
if (u != v) {
types[index] = v;
return true;
}
return false;
}
}
================================================
FILE: src/jvm/clojure/asm/Handle.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* A reference to a field or a method.
*
* @author Remi Forax
* @author Eric Bruneton
*/
public final class Handle {
/**
* The kind of field or method designated by this Handle. Should be
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
* {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
*/
final int tag;
/**
* The internal name of the field or method designed by this handle.
*/
final String owner;
/**
* The name of the field or method designated by this handle.
*/
final String name;
/**
* The descriptor of the field or method designated by this handle.
*/
final String desc;
/**
* Constructs a new field or method handle.
*
* @param tag
* the kind of field or method designated by this Handle. Must be
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
* {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the field or method designed by this
* handle.
* @param name
* the name of the field or method designated by this handle.
* @param desc
* the descriptor of the field or method designated by this
* handle.
*/
public Handle(int tag, String owner, String name, String desc) {
this.tag = tag;
this.owner = owner;
this.name = name;
this.desc = desc;
}
/**
* Returns the kind of field or method designated by this handle.
*
* @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
* {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
*/
public int getTag() {
return tag;
}
/**
* Returns the internal name of the field or method designed by this handle.
*
* @return the internal name of the field or method designed by this handle.
*/
public String getOwner() {
return owner;
}
/**
* Returns the name of the field or method designated by this handle.
*
* @return the name of the field or method designated by this handle.
*/
public String getName() {
return name;
}
/**
* Returns the descriptor of the field or method designated by this handle.
*
* @return the descriptor of the field or method designated by this handle.
*/
public String getDesc() {
return desc;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Handle)) {
return false;
}
Handle h = (Handle) obj;
return tag == h.tag && owner.equals(h.owner) && name.equals(h.name)
&& desc.equals(h.desc);
}
@Override
public int hashCode() {
return tag + owner.hashCode() * name.hashCode() * desc.hashCode();
}
/**
* Returns the textual representation of this handle. The textual
* representation is:
*
*
* owner '.' name desc ' ' '(' tag ')'
*
*
* . As this format is unambiguous, it can be parsed if necessary.
*/
@Override
public String toString() {
return owner + '.' + name + desc + " (" + tag + ')';
}
}
================================================
FILE: src/jvm/clojure/asm/Handler.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* Information about an exception handler block.
*
* @author Eric Bruneton
*/
class Handler {
/**
* Beginning of the exception handler's scope (inclusive).
*/
Label start;
/**
* End of the exception handler's scope (exclusive).
*/
Label end;
/**
* Beginning of the exception handler's code.
*/
Label handler;
/**
* Internal name of the type of exceptions handled by this handler, or
* null to catch any exceptions.
*/
String desc;
/**
* Constant pool index of the internal name of the type of exceptions
* handled by this handler, or 0 to catch any exceptions.
*/
int type;
/**
* Next exception handler block info.
*/
Handler next;
/**
* Removes the range between start and end from the given exception
* handlers.
*
* @param h
* an exception handler list.
* @param start
* the start of the range to be removed.
* @param end
* the end of the range to be removed. Maybe null.
* @return the exception handler list with the start-end range removed.
*/
static Handler remove(Handler h, Label start, Label end) {
if (h == null) {
return null;
} else {
h.next = remove(h.next, start, end);
}
int hstart = h.start.position;
int hend = h.end.position;
int s = start.position;
int e = end == null ? Integer.MAX_VALUE : end.position;
// if [hstart,hend[ and [s,e[ intervals intersect...
if (s < hend && e > hstart) {
if (s <= hstart) {
if (e >= hend) {
// [hstart,hend[ fully included in [s,e[, h removed
h = h.next;
} else {
// [hstart,hend[ minus [s,e[ = [e,hend[
h.start = end;
}
} else if (e >= hend) {
// [hstart,hend[ minus [s,e[ = [hstart,s[
h.end = start;
} else {
// [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[
Handler g = new Handler();
g.start = end;
g.end = h.end;
g.handler = h.handler;
g.desc = h.desc;
g.type = h.type;
g.next = h.next;
h.end = start;
h.next = g;
}
}
return h;
}
}
================================================
FILE: src/jvm/clojure/asm/Item.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* A constant pool item. Constant pool items can be created with the 'newXXX'
* methods in the {@link ClassWriter} class.
*
* @author Eric Bruneton
*/
final class Item {
/**
* Index of this item in the constant pool.
*/
int 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 {@link ClassWriter#INT},
* {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT},
* {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8},
* {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
* {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
* {@link ClassWriter#METH}, {@link ClassWriter#IMETH},
* {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}.
*
* MethodHandle constant 9 variations are stored using a range of 9 values
* from {@link ClassWriter#HANDLE_BASE} + 1 to
* {@link ClassWriter#HANDLE_BASE} + 9.
*
* Special Item types are used for Items that are stored in the ClassWriter
* {@link ClassWriter#typeTable}, instead of the constant pool, in order to
* avoid clashes with normal constant pool items in the ClassWriter constant
* pool's hash table. These special item types are
* {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and
* {@link ClassWriter#TYPE_MERGED}.
*/
int type;
/**
* Value of this item, for an integer item.
*/
int intVal;
/**
* Value of this item, for a long item.
*/
long longVal;
/**
* 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() {
}
/**
* Constructs an uninitialized {@link Item} for constant pool element at
* given position.
*
* @param index
* index of the item to be constructed.
*/
Item(final int index) {
this.index = index;
}
/**
* 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 int index, final Item i) {
this.index = index;
type = i.type;
intVal = i.intVal;
longVal = i.longVal;
strVal1 = i.strVal1;
strVal2 = i.strVal2;
strVal3 = i.strVal3;
hashCode = i.hashCode;
}
/**
* Sets this item to an integer 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 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 float item.
*
* @param floatVal
* the value of this item.
*/
void set(final float floatVal) {
this.type = ClassWriter.FLOAT;
this.intVal = Float.floatToRawIntBits(floatVal);
this.hashCode = 0x7FFFFFFF & (type + (int) floatVal);
}
/**
* Sets this item to a double item.
*
* @param doubleVal
* the value of this item.
*/
void set(final double doubleVal) {
this.type = ClassWriter.DOUBLE;
this.longVal = Double.doubleToRawLongBits(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:
case ClassWriter.MTYPE:
case ClassWriter.TYPE_NORMAL:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
return;
case ClassWriter.NAME_TYPE: {
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
* strVal2.hashCode());
return;
}
// ClassWriter.FIELD:
// ClassWriter.METH:
// ClassWriter.IMETH:
// ClassWriter.HANDLE_BASE + 1..9
default:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
* strVal2.hashCode() * strVal3.hashCode());
}
}
/**
* Sets the item to an InvokeDynamic item.
*
* @param name
* invokedynamic's name.
* @param desc
* invokedynamic's desc.
* @param bsmIndex
* zero based index into the class attribute BootrapMethods.
*/
void set(String name, String desc, int bsmIndex) {
this.type = ClassWriter.INDY;
this.longVal = bsmIndex;
this.strVal1 = name;
this.strVal2 = desc;
this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex
* strVal1.hashCode() * strVal2.hashCode());
}
/**
* Sets the item to a BootstrapMethod item.
*
* @param position
* position in byte in the class attribute BootrapMethods.
* @param hashCode
* hashcode of the item. This hashcode is processed from the
* hashcode of the bootstrap method and the hashcode of all
* bootstrap arguments.
*/
void set(int position, int hashCode) {
this.type = ClassWriter.BSM;
this.intVal = position;
this.hashCode = hashCode;
}
/**
* Indicates if the given item is equal to this one. This method assumes
* that the two items have the same {@link #type}.
*
* @param i
* the item to be compared to this one. Both items must have the
* same {@link #type}.
* @return true if the given item if equal to this one,
* false otherwise.
*/
boolean isEqualTo(final Item i) {
switch (type) {
case ClassWriter.UTF8:
case ClassWriter.STR:
case ClassWriter.CLASS:
case ClassWriter.MTYPE:
case ClassWriter.TYPE_NORMAL:
return i.strVal1.equals(strVal1);
case ClassWriter.TYPE_MERGED:
case ClassWriter.LONG:
case ClassWriter.DOUBLE:
return i.longVal == longVal;
case ClassWriter.INT:
case ClassWriter.FLOAT:
return i.intVal == intVal;
case ClassWriter.TYPE_UNINIT:
return i.intVal == intVal && i.strVal1.equals(strVal1);
case ClassWriter.NAME_TYPE:
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2);
case ClassWriter.INDY: {
return i.longVal == longVal && i.strVal1.equals(strVal1)
&& i.strVal2.equals(strVal2);
}
// case ClassWriter.FIELD:
// case ClassWriter.METH:
// case ClassWriter.IMETH:
// case ClassWriter.HANDLE_BASE + 1..9
default:
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2)
&& i.strVal3.equals(strVal3);
}
}
}
================================================
FILE: src/jvm/clojure/asm/Label.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.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. A label
* designates the instruction that is just after. Note however that there
* can be other elements between a label and the instruction it designates (such
* as other labels, stack map frames, line numbers, etc.).
*
* @author Eric Bruneton
*/
public class Label {
/**
* Indicates if this label is only used for debug attributes. Such a label
* is not the start of a basic block, the target of a jump instruction, or
* an exception handler. It can be safely ignored in control flow graph
* analysis algorithms (for optimization purposes).
*/
static final int DEBUG = 1;
/**
* Indicates if the position of this label is known.
*/
static final int RESOLVED = 2;
/**
* Indicates if this label has been updated, after instruction resizing.
*/
static final int RESIZED = 4;
/**
* Indicates if this basic block has been pushed in the basic block stack.
* See {@link MethodWriter#visitMaxs visitMaxs}.
*/
static final int PUSHED = 8;
/**
* Indicates if this label is the target of a jump instruction, or the start
* of an exception handler.
*/
static final int TARGET = 16;
/**
* Indicates if a stack map frame must be stored for this label.
*/
static final int STORE = 32;
/**
* Indicates if this label corresponds to a reachable basic block.
*/
static final int REACHABLE = 64;
/**
* Indicates if this basic block ends with a JSR instruction.
*/
static final int JSR = 128;
/**
* Indicates if this basic block ends with a RET instruction.
*/
static final int RET = 256;
/**
* Indicates if this basic block is the start of a subroutine.
*/
static final int SUBROUTINE = 512;
/**
* Indicates if this subroutine basic block has been visited by a
* visitSubroutine(null, ...) call.
*/
static final int VISITED = 1024;
/**
* Indicates if this subroutine basic block has been visited by a
* visitSubroutine(!null, ...) call.
*/
static final int VISITED2 = 2048;
/**
* Field used to associate user information to a label. Warning: this field
* is used by the ASM tree package. In order to use it with the ASM tree
* package you must override the
* {@link clojure.asm.tree.MethodNode#getLabelNode} method.
*/
public Object info;
/**
* Flags that indicate the status of this label.
*
* @see #DEBUG
* @see #RESOLVED
* @see #RESIZED
* @see #PUSHED
* @see #TARGET
* @see #STORE
* @see #REACHABLE
* @see #JSR
* @see #RET
*/
int status;
/**
* The line number corresponding to this label, if known.
*/
int line;
/**
* The position of this label in the code, if known.
*/
int position;
/**
* 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. This array is also used
* as a bitset to store the subroutines to which a basic block belongs. This
* information is needed in {@linked MethodWriter#visitMaxs}, after all
* forward references have been resolved. Hence the same array can be used
* for both purposes without problems.
*/
private int[] srcAndRefPositions;
// ------------------------------------------------------------------------
/*
* Fields for the control flow and data flow graph analysis algorithms (used
* to compute the maximum stack size or the stack map frames). 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.
*
* The control flow analysis algorithms used to compute the maximum stack
* size or the stack map frames are similar and use two steps. The first
* step, during the visit of each instruction, builds information about the
* state of the local variables and the operand stack at the end of each
* basic block, called the "output frame", relatively to the frame
* state at the beginning of the basic block, which is called the "input
* frame", and which is unknown during this step. The second step, in
* {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes
* information about the input frame of each basic block, from the input
* state of the first basic block (known from the method signature), and by
* the using the previously computed relative output frames.
*
* The algorithm used to compute the maximum stack size only computes the
* relative output and absolute input stack heights, while the algorithm
* used to compute stack map frames computes relative output frames and
* absolute input frames.
*/
/**
* Start of the output stack relatively to the input stack. The exact
* semantics of this field depends on the algorithm that is used.
*
* When only the maximum stack size is computed, this field is the number of
* elements in the input stack.
*
* When the stack map frames are completely computed, this field is the
* offset of the first output stack element relatively to the top of the
* input stack. This offset is always negative or null. A null offset means
* that the output stack must be appended to the input stack. A -n offset
* means that the first n output stack elements must replace the top n input
* stack elements, and that the other elements must be appended to the input
* stack.
*/
int inputStackTop;
/**
* Maximum height reached by the output stack, relatively to the top of the
* input stack. This maximum is always positive or null.
*/
int outputStackMax;
/**
* Information about the input and output stack map frames of this basic
* block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
* option is used.
*/
Frame frame;
/**
* The successor of this label, in the order they are visited. This linked
* list does not include labels used for debug info only. If
* {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
* does not contain successive labels that denote the same bytecode position
* (in this case only the first label appears in this list).
*/
Label successor;
/**
* 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. This stack is used in the
* main loop of the fix point algorithm used in the second step of the
* control flow analysis algorithms. It is also used in
* {@link #visitSubroutine} to avoid using a recursive method.
*
* @see MethodWriter#visitMaxs
*/
Label next;
// ------------------------------------------------------------------------
// 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 ((status & RESOLVED) == 0) {
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 MethodWriter owner, final ByteVector out, final int source,
final boolean wideOffset) {
if ((status & RESOLVED) == 0) {
if (wideOffset) {
addReference(-1 - source, out.length);
out.putInt(-1);
} else {
addReference(source, out.length);
out.putShort(-1);
}
} else {
if (wideOffset) {
out.putInt(position - source);
} else {
out.putShort(position - source);
}
}
}
/**
* 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 MethodWriter#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 MethodWriter owner, final int position,
final byte[] data) {
boolean needUpdate = false;
this.status |= RESOLVED;
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
* MethodWriter). 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 <= Opcodes.JSR) {
// changes IFEQ ... JSR to opcodes 202 to 217
data[reference - 1] = (byte) (opcode + 49);
} else {
// changes IFNULL and IFNONNULL to opcodes 218 and 219
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;
}
/**
* Returns the first label of the series to which this label belongs. For an
* isolated label or for the first label in a series of successive labels,
* this method returns the label itself. For other labels it returns the
* first label of the series.
*
* @return the first label of the series to which this label belongs.
*/
Label getFirst() {
return !ClassReader.FRAMES || frame == null ? this : frame.owner;
}
// ------------------------------------------------------------------------
// Methods related to subroutines
// ------------------------------------------------------------------------
/**
* Returns true is this basic block belongs to the given subroutine.
*
* @param id
* a subroutine id.
* @return true is this basic block belongs to the given subroutine.
*/
boolean inSubroutine(final long id) {
if ((status & Label.VISITED) != 0) {
return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0;
}
return false;
}
/**
* Returns true if this basic block and the given one belong to a common
* subroutine.
*
* @param block
* another basic block.
* @return true if this basic block and the given one belong to a common
* subroutine.
*/
boolean inSameSubroutine(final Label block) {
if ((status & VISITED) == 0 || (block.status & VISITED) == 0) {
return false;
}
for (int i = 0; i < srcAndRefPositions.length; ++i) {
if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) {
return true;
}
}
return false;
}
/**
* Marks this basic block as belonging to the given subroutine.
*
* @param id
* a subroutine id.
* @param nbSubroutines
* the total number of subroutines in the method.
*/
void addToSubroutine(final long id, final int nbSubroutines) {
if ((status & VISITED) == 0) {
status |= VISITED;
srcAndRefPositions = new int[(nbSubroutines - 1) / 32 + 1];
}
srcAndRefPositions[(int) (id >>> 32)] |= (int) id;
}
/**
* Finds the basic blocks that belong to a given subroutine, and marks these
* blocks as belonging to this subroutine. This method follows the control
* flow graph to find all the blocks that are reachable from the current
* block WITHOUT following any JSR target.
*
* @param JSR
* a JSR block that jumps to this subroutine. If this JSR is not
* null it is added to the successor of the RET blocks found in
* the subroutine.
* @param id
* the id of this subroutine.
* @param nbSubroutines
* the total number of subroutines in the method.
*/
void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) {
// user managed stack of labels, to avoid using a recursive method
// (recursivity can lead to stack overflow with very large methods)
Label stack = this;
while (stack != null) {
// removes a label l from the stack
Label l = stack;
stack = l.next;
l.next = null;
if (JSR != null) {
if ((l.status & VISITED2) != 0) {
continue;
}
l.status |= VISITED2;
// adds JSR to the successors of l, if it is a RET block
if ((l.status & RET) != 0) {
if (!l.inSameSubroutine(JSR)) {
Edge e = new Edge();
e.info = l.inputStackTop;
e.successor = JSR.successors.successor;
e.next = l.successors;
l.successors = e;
}
}
} else {
// if the l block already belongs to subroutine 'id', continue
if (l.inSubroutine(id)) {
continue;
}
// marks the l block as belonging to subroutine 'id'
l.addToSubroutine(id, nbSubroutines);
}
// pushes each successor of l on the stack, except JSR targets
Edge e = l.successors;
while (e != null) {
// if the l block is a JSR block, then 'l.successors.next' leads
// to the JSR target (see {@link #visitJumpInsn}) and must
// therefore not be followed
if ((l.status & Label.JSR) == 0 || e != l.successors.next) {
// pushes e.successor on the stack if it not already added
if (e.successor.next == null) {
e.successor.next = stack;
stack = e.successor;
}
}
e = e.next;
}
}
}
// ------------------------------------------------------------------------
// Overriden Object methods
// ------------------------------------------------------------------------
/**
* Returns a string representation of this label.
*
* @return a string representation of this label.
*/
@Override
public String toString() {
return "L" + System.identityHashCode(this);
}
}
================================================
FILE: src/jvm/clojure/asm/MethodVisitor.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* A visitor to visit a Java method. The methods of this class must be called in
* the following order: [ visitAnnotationDefault ] (
* visitAnnotation | visitParameterAnnotation |
* visitAttribute )* [ visitCode ( visitFrame |
* visitXInsn | visitLabel |
* visitTryCatchBlock | visitLocalVariable |
* visitLineNumber )* visitMaxs ] visitEnd. In
* addition, the visitXInsn and visitLabel methods
* must be called in the sequential order of the bytecode instructions of the
* visited code, visitTryCatchBlock must be called before the
* labels passed as arguments have been visited, and the
* visitLocalVariable and visitLineNumber methods must be
* called after the labels passed as arguments have been visited.
*
* @author Eric Bruneton
*/
public abstract class MethodVisitor {
/**
* The ASM API version implemented by this visitor. The value of this field
* must be one of {@link Opcodes#ASM4}.
*/
protected final int api;
/**
* The method visitor to which this visitor must delegate method calls. May
* be null.
*/
protected MethodVisitor mv;
/**
* Constructs a new {@link MethodVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
*/
public MethodVisitor(final int api) {
this(api, null);
}
/**
* Constructs a new {@link MethodVisitor}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param mv
* the method visitor to which this visitor must delegate method
* calls. May be null.
*/
public MethodVisitor(final int api, final MethodVisitor mv) {
if (api != Opcodes.ASM4) {
throw new IllegalArgumentException();
}
this.api = api;
this.mv = mv;
}
// -------------------------------------------------------------------------
// Annotations and non standard attributes
// -------------------------------------------------------------------------
/**
* Visits the default value of this annotation interface method.
*
* @return a visitor to the visit the actual default value of this
* annotation interface method, or null if this visitor is
* not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this
* annotation visitor, followed by visitEnd.
*/
public AnnotationVisitor visitAnnotationDefault() {
if (mv != null) {
return mv.visitAnnotationDefault();
}
return null;
}
/**
* Visits an annotation of this method.
*
* @param desc
* the class descriptor of the annotation class.
* @param visible
* true if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or null if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (mv != null) {
return mv.visitAnnotation(desc, visible);
}
return null;
}
/**
* Visits an annotation of a parameter this method.
*
* @param parameter
* the parameter index.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* true if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or null if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitParameterAnnotation(int parameter,
String desc, boolean visible) {
if (mv != null) {
return mv.visitParameterAnnotation(parameter, desc, visible);
}
return null;
}
/**
* Visits a non standard attribute of this method.
*
* @param attr
* an attribute.
*/
public void visitAttribute(Attribute attr) {
if (mv != null) {
mv.visitAttribute(attr);
}
}
/**
* Starts the visit of the method's code, if any (i.e. non abstract method).
*/
public void visitCode() {
if (mv != null) {
mv.visitCode();
}
}
/**
* Visits the current state of the local variables and operand stack
* elements. This method must(*) be called just before any
* instruction i that follows an unconditional branch instruction
* such as GOTO or THROW, that is the target of a jump instruction, or that
* starts an exception handler block. The visited types must describe the
* values of the local variables and of the operand stack elements just
* beforei is executed.
*
* (*) this is mandatory only for classes whose version is greater than or
* equal to {@link Opcodes#V1_6 V1_6}.
*
* The frames of a method must be given either in expanded form, or in
* compressed form (all frames must use the same format, i.e. you must not
* mix expanded and compressed frames within a single method):
*
*
In expanded form, all frames must have the F_NEW type.
*
In compressed form, frames are basically "deltas" from the state of
* the previous frame:
*
*
{@link Opcodes#F_SAME} representing frame with exactly the same
* locals as the previous frame and with the empty stack.
*
{@link Opcodes#F_SAME1} representing frame with exactly the same
* locals as the previous frame and with single value on the stack (
* nStack is 1 and stack[0] contains value for the
* type of the stack item).
*
{@link Opcodes#F_APPEND} representing frame with current locals are
* the same as the locals in the previous frame, except that additional
* locals are defined (nLocal is 1, 2 or 3 and
* local elements contains values representing added types).
*
{@link Opcodes#F_CHOP} representing frame with current locals are the
* same as the locals in the previous frame, except that the last 1-3 locals
* are absent and with the empty stack (nLocals is 1, 2 or 3).
*
* In both cases the first frame, corresponding to the method's parameters
* and access flags, is implicit and must not be visited. Also, it is
* illegal to visit two or more frames for the same code location (i.e., at
* least one instruction must be visited between two calls to visitFrame).
*
* @param type
* the type of this stack map frame. Must be
* {@link Opcodes#F_NEW} for expanded frames, or
* {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND},
* {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or
* {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for
* compressed frames.
* @param nLocal
* the number of local variables in the visited frame.
* @param local
* the local variable types in this frame. This array must not be
* modified. Primitive types are represented by
* {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
* {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
* {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are
* represented by a single element). Reference types are
* represented by String objects (representing internal names),
* and uninitialized types by Label objects (this label
* designates the NEW instruction that created this uninitialized
* value).
* @param nStack
* the number of operand stack elements in the visited frame.
* @param stack
* the operand stack types in this frame. This array must not be
* modified. Its content has the same format as the "local"
* array.
* @throws IllegalStateException
* if a frame is visited just after another one, without any
* instruction between the two (unless this frame is a
* Opcodes#F_SAME frame, in which case it is silently ignored).
*/
public void visitFrame(int type, int nLocal, Object[] local, int nStack,
Object[] stack) {
if (mv != null) {
mv.visitFrame(type, nLocal, local, nStack, stack);
}
}
// -------------------------------------------------------------------------
// Normal instructions
// -------------------------------------------------------------------------
/**
* 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.
*/
public void visitInsn(int opcode) {
if (mv != null) {
mv.visitInsn(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.
* When opcode is BIPUSH, operand value should be between
* Byte.MIN_VALUE and Byte.MAX_VALUE.
* When opcode is SIPUSH, operand value should be between
* Short.MIN_VALUE and Short.MAX_VALUE.
* When opcode is NEWARRAY, operand value should be one of
* {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR},
* {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE},
* {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT},
* {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}.
*/
public void visitIntInsn(int opcode, int operand) {
if (mv != null) {
mv.visitIntInsn(opcode, 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.
*/
public void visitVarInsn(int opcode, int var) {
if (mv != null) {
mv.visitVarInsn(opcode, var);
}
}
/**
* Visits a type instruction. A type instruction is an instruction that
* takes the internal name of a class as parameter.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
* @param type
* the operand of the instruction to be visited. This operand
* must be the internal name of an object or array class (see
* {@link Type#getInternalName() getInternalName}).
*/
public void visitTypeInsn(int opcode, String type) {
if (mv != null) {
mv.visitTypeInsn(opcode, type);
}
}
/**
* 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}).
*/
public void visitFieldInsn(int opcode, String owner, String name,
String desc) {
if (mv != null) {
mv.visitFieldInsn(opcode, owner, name, 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}).
*/
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
}
/**
* Visits an invokedynamic instruction.
*
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param bsm
* the bootstrap method.
* @param bsmArgs
* the bootstrap method constant arguments. Each argument must be
* an {@link Integer}, {@link Float}, {@link Long},
* {@link Double}, {@link String}, {@link Type} or {@link Handle}
* value. This method is allowed to modify the content of the
* array so a caller should expect that this array may change.
*/
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
if (mv != null) {
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
}
/**
* 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.
*/
public void visitJumpInsn(int opcode, Label label) {
if (mv != null) {
mv.visitJumpInsn(opcode, label);
}
}
/**
* Visits a label. A label designates the instruction that will be visited
* just after it.
*
* @param label
* a {@link Label Label} object.
*/
public void visitLabel(Label label) {
if (mv != null) {
mv.visitLabel(label);
}
}
// -------------------------------------------------------------------------
// Special instructions
// -------------------------------------------------------------------------
/**
* Visits a LDC instruction. Note that new constant types may be added in
* future versions of the Java Virtual Machine. To easily detect new
* constant types, implementations of this method should check for
* unexpected constant types, like this:
*
*
*
* @param cst
* the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for .class constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0.
*/
public void visitLdcInsn(Object cst) {
if (mv != null) {
mv.visitLdcInsn(cst);
}
}
/**
* Visits an IINC instruction.
*
* @param var
* index of the local variable to be incremented.
* @param increment
* amount to increment the local variable by.
*/
public void visitIincInsn(int var, int increment) {
if (mv != null) {
mv.visitIincInsn(var, 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.
*/
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) {
if (mv != null) {
mv.visitTableSwitchInsn(min, max, dflt, 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.
*/
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) {
mv.visitLookupSwitchInsn(dflt, keys, 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.
*/
public void visitMultiANewArrayInsn(String desc, int dims) {
if (mv != null) {
mv.visitMultiANewArrayInsn(desc, dims);
}
}
// -------------------------------------------------------------------------
// Exceptions table entries, debug information, max stack 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 already been visited by this visitor
* (by the {@link #visitLabel visitLabel} method).
*/
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type) {
if (mv != null) {
mv.visitTryCatchBlock(start, end, handler, type);
}
}
/**
* Visits a local variable declaration.
*
* @param name
* the name of a local variable.
* @param desc
* the type descriptor of this local variable.
* @param signature
* the type signature of this local variable. May be
* null if the local variable type does not use generic
* types.
* @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).
*/
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
if (mv != null) {
mv.visitLocalVariable(name, desc, signature, start, end, 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).
*/
public void visitLineNumber(int line, Label start) {
if (mv != null) {
mv.visitLineNumber(line, start);
}
}
/**
* 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.
*/
public void visitMaxs(int maxStack, int maxLocals) {
if (mv != null) {
mv.visitMaxs(maxStack, maxLocals);
}
}
/**
* Visits the end of the method. This method, which is the last one to be
* called, is used to inform the visitor that all the annotations and
* attributes of the method have been visited.
*/
public void visitEnd() {
if (mv != null) {
mv.visitEnd();
}
}
}
================================================
FILE: src/jvm/clojure/asm/MethodWriter.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
/**
* A {@link MethodVisitor} that generates methods in bytecode form. 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.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
class MethodWriter extends MethodVisitor {
/**
* Pseudo access flag used to denote constructors.
*/
static final int ACC_CONSTRUCTOR = 0x80000;
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is zero.
*/
static final int SAME_FRAME = 0; // to 63 (0-3f)
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is 1
*/
static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
/**
* Reserved for future use
*/
static final int RESERVED = 128;
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is 1. Offset is bigger then 63;
*/
static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
/**
* Frame where current locals are the same as the locals in the previous
* frame, except that the k last locals are absent. The value of k is given
* by the formula 251-frame_type.
*/
static final int CHOP_FRAME = 248; // to 250 (f8-fA)
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is zero. Offset is bigger then 63;
*/
static final int SAME_FRAME_EXTENDED = 251; // fb
/**
* Frame where current locals are the same as the locals in the previous
* frame, except that k additional locals are defined. The value of k is
* given by the formula frame_type-251.
*/
static final int APPEND_FRAME = 252; // to 254 // fc-fe
/**
* Full frame
*/
static final int FULL_FRAME = 255; // ff
/**
* Indicates that the stack map frames must be recomputed from scratch. In
* this case the maximum stack size and number of local variables is also
* recomputed from scratch.
*
* @see #compute
*/
private static final int FRAMES = 0;
/**
* Indicates that the maximum stack size and number of local variables must
* be automatically computed.
*
* @see #compute
*/
private static final int MAXS = 1;
/**
* Indicates that nothing must be automatically computed.
*
* @see #compute
*/
private static final int NOTHING = 2;
/**
* The class writer to which this method must be added.
*/
final ClassWriter cw;
/**
* Access flags of this method.
*/
private int access;
/**
* The index of the constant pool item that contains the name of this
* method.
*/
private final int name;
/**
* The index of the constant pool item that contains the descriptor of this
* method.
*/
private final int desc;
/**
* The descriptor of this method.
*/
private final String descriptor;
/**
* The signature of this method.
*/
String signature;
/**
* If not zero, indicates that the code of this method must be copied from
* the ClassReader associated to this writer in cw.cr. More
* precisely, this field gives the index of the first byte to copied from
* cw.cr.b.
*/
int classReaderOffset;
/**
* If not zero, indicates that the code of this method must be copied from
* the ClassReader associated to this writer in cw.cr. More
* precisely, this field gives the number of bytes to copied from
* cw.cr.b.
*/
int classReaderLength;
/**
* Number of exceptions that can be thrown by this method.
*/
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.
*/
int[] exceptions;
/**
* The annotation default attribute of this method. May be null.
*/
private ByteVector annd;
/**
* The runtime visible annotations of this method. May be null.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this method. May be null.
*/
private AnnotationWriter ianns;
/**
* The runtime visible parameter annotations of this method. May be
* null.
*/
private AnnotationWriter[] panns;
/**
* The runtime invisible parameter annotations of this method. May be
* null.
*/
private AnnotationWriter[] ipanns;
/**
* The number of synthetic parameters of this method.
*/
private int synthetics;
/**
* The non standard attributes of the method.
*/
private Attribute attrs;
/**
* The bytecode of this method.
*/
private ByteVector code = new ByteVector();
/**
* Maximum stack size of this method.
*/
private int maxStack;
/**
* Maximum number of local variables for this method.
*/
private int maxLocals;
/**
* Number of local variables in the current stack map frame.
*/
private int currentLocals;
/**
* Number of stack map frames in the StackMapTable attribute.
*/
private int frameCount;
/**
* The StackMapTable attribute.
*/
private ByteVector stackMap;
/**
* The offset of the last frame that was written in the StackMapTable
* attribute.
*/
private int previousFrameOffset;
/**
* The last frame that was written in the StackMapTable attribute.
*
* @see #frame
*/
private int[] previousFrame;
/**
* The current stack map frame. The first element contains the offset of the
* instruction to which the frame corresponds, the second element is the
* number of locals and the third one is the number of stack elements. The
* local variables start at index 3 and are followed by the operand stack
* values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
* nStack, frame[3] = nLocal. All types are encoded as integers, with the
* same format as the one used in {@link Label}, but limited to BASE types.
*/
private int[] frame;
/**
* Number of elements in the exception handler list.
*/
private int handlerCount;
/**
* The first element in the exception handler list.
*/
private Handler firstHandler;
/**
* The last element in the exception handler list.
*/
private Handler lastHandler;
/**
* Number of entries in the LocalVariableTable attribute.
*/
private int localVarCount;
/**
* The LocalVariableTable attribute.
*/
private ByteVector localVar;
/**
* Number of entries in the LocalVariableTypeTable attribute.
*/
private int localVarTypeCount;
/**
* The LocalVariableTypeTable attribute.
*/
private ByteVector localVarType;
/**
* 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;
/**
* The number of subroutines in this method.
*/
private int subroutines;
// ------------------------------------------------------------------------
/*
* 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.
*/
/**
* Indicates what must be automatically computed.
*
* @see #FRAMES
* @see #MAXS
* @see #NOTHING
*/
private final int compute;
/**
* A list of labels. This list is the list of basic blocks in the method,
* i.e. a list of Label objects linked to each other by their
* {@link Label#successor} field, in the order they are visited by
* {@link MethodVisitor#visitLabel}, and starting with the first basic
* block.
*/
private Label labels;
/**
* The previous basic block.
*/
private Label previousBlock;
/**
* The current basic block.
*/
private Label currentBlock;
/**
* 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#inputStackTop 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#inputStackTop beginStackSize} of the current basic
* block plus stackSize.
*/
private int maxStackSize;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new {@link MethodWriter}.
*
* @param cw
* the class writer in which the method must be added.
* @param access
* the method's access flags (see {@link Opcodes}).
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be null.
* @param exceptions
* the internal names of the method's exceptions. May be
* null.
* @param computeMaxs
* true if the maximum stack size and number of local
* variables must be automatically computed.
* @param computeFrames
* true if the stack map tables must be recomputed from
* scratch.
*/
MethodWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature,
final String[] exceptions, final boolean computeMaxs,
final boolean computeFrames) {
super(Opcodes.ASM4);
if (cw.firstMethod == null) {
cw.firstMethod = this;
} else {
cw.lastMethod.mv = this;
}
cw.lastMethod = this;
this.cw = cw;
this.access = access;
if ("".equals(name)) {
this.access |= ACC_CONSTRUCTOR;
}
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
this.descriptor = desc;
if (ClassReader.SIGNATURES) {
this.signature = signature;
}
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.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
if (computeMaxs || computeFrames) {
// updates maxLocals
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
if ((access & Opcodes.ACC_STATIC) != 0) {
--size;
}
maxLocals = size;
currentLocals = size;
// creates and visits the label for the first basic block
labels = new Label();
labels.status |= Label.PUSHED;
visitLabel(labels);
}
}
// ------------------------------------------------------------------------
// Implementation of the MethodVisitor abstract class
// ------------------------------------------------------------------------
@Override
public AnnotationVisitor visitAnnotationDefault() {
if (!ClassReader.ANNOTATIONS) {
return null;
}
annd = new ByteVector();
return new AnnotationWriter(cw, false, annd, null, 0);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector();
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
if (visible) {
aw.next = anns;
anns = aw;
} else {
aw.next = ianns;
ianns = aw;
}
return aw;
}
@Override
public AnnotationVisitor visitParameterAnnotation(final int parameter,
final String desc, final boolean visible) {
if (!ClassReader.ANNOTATIONS) {
return null;
}
ByteVector bv = new ByteVector();
if ("Ljava/lang/Synthetic;".equals(desc)) {
// workaround for a bug in javac with synthetic parameters
// see ClassReader.readParameterAnnotations
synthetics = Math.max(synthetics, parameter + 1);
return new AnnotationWriter(cw, false, bv, null, 0);
}
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
if (visible) {
if (panns == null) {
panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
aw.next = panns[parameter];
panns[parameter] = aw;
} else {
if (ipanns == null) {
ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
aw.next = ipanns[parameter];
ipanns[parameter] = aw;
}
return aw;
}
@Override
public void visitAttribute(final Attribute attr) {
if (attr.isCodeAttribute()) {
attr.next = cattrs;
cattrs = attr;
} else {
attr.next = attrs;
attrs = attr;
}
}
@Override
public void visitCode() {
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
if (!ClassReader.FRAMES || compute == FRAMES) {
return;
}
if (type == Opcodes.F_NEW) {
if (previousFrame == null) {
visitImplicitFirstFrame();
}
currentLocals = nLocal;
int frameIndex = startFrame(code.length, nLocal, nStack);
for (int i = 0; i < nLocal; ++i) {
if (local[i] instanceof String) {
frame[frameIndex++] = Frame.OBJECT
| cw.addType((String) local[i]);
} else if (local[i] instanceof Integer) {
frame[frameIndex++] = ((Integer) local[i]).intValue();
} else {
frame[frameIndex++] = Frame.UNINITIALIZED
| cw.addUninitializedType("",
((Label) local[i]).position);
}
}
for (int i = 0; i < nStack; ++i) {
if (stack[i] instanceof String) {
frame[frameIndex++] = Frame.OBJECT
| cw.addType((String) stack[i]);
} else if (stack[i] instanceof Integer) {
frame[frameIndex++] = ((Integer) stack[i]).intValue();
} else {
frame[frameIndex++] = Frame.UNINITIALIZED
| cw.addUninitializedType("",
((Label) stack[i]).position);
}
}
endFrame();
} else {
int delta;
if (stackMap == null) {
stackMap = new ByteVector();
delta = code.length;
} else {
delta = code.length - previousFrameOffset - 1;
if (delta < 0) {
if (type == Opcodes.F_SAME) {
return;
} else {
throw new IllegalStateException();
}
}
}
switch (type) {
case Opcodes.F_FULL:
currentLocals = nLocal;
stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal);
for (int i = 0; i < nLocal; ++i) {
writeFrameType(local[i]);
}
stackMap.putShort(nStack);
for (int i = 0; i < nStack; ++i) {
writeFrameType(stack[i]);
}
break;
case Opcodes.F_APPEND:
currentLocals += nLocal;
stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta);
for (int i = 0; i < nLocal; ++i) {
writeFrameType(local[i]);
}
break;
case Opcodes.F_CHOP:
currentLocals -= nLocal;
stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta);
break;
case Opcodes.F_SAME:
if (delta < 64) {
stackMap.putByte(delta);
} else {
stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
}
break;
case Opcodes.F_SAME1:
if (delta < 64) {
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
} else {
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
.putShort(delta);
}
writeFrameType(stack[0]);
break;
}
previousFrameOffset = code.length;
++frameCount;
}
maxStack = Math.max(maxStack, nStack);
maxLocals = Math.max(maxLocals, currentLocals);
}
@Override
public void visitInsn(final int opcode) {
// adds the instruction to the bytecode of the method
code.putByte(opcode);
// update currentBlock
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
} else {
// updates current and max stack sizes
int size = stackSize + Frame.SIZE[opcode];
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// if opcode == ATHROW or xRETURN, ends current block (no successor)
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
|| opcode == Opcodes.ATHROW) {
noSuccessor();
}
}
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, operand, null, null);
} else if (opcode != Opcodes.NEWARRAY) {
// updates current and max stack sizes only for 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 == Opcodes.SIPUSH) {
code.put12(opcode, operand);
} else { // BIPUSH or NEWARRAY
code.put11(opcode, operand);
}
}
@Override
public void visitVarInsn(final int opcode, final int var) {
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, var, null, null);
} else {
// updates current and max stack sizes
if (opcode == Opcodes.RET) {
// no stack change, but end of current block (no successor)
currentBlock.status |= Label.RET;
// save 'stackSize' here for future use
// (see {@link #findSubroutineSuccessors})
currentBlock.inputStackTop = stackSize;
noSuccessor();
} else { // xLOAD or xSTORE
int size = stackSize + Frame.SIZE[opcode];
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
}
if (compute != NOTHING) {
// updates max locals
int n;
if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
|| opcode == Opcodes.LSTORE || opcode == Opcodes.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 != Opcodes.RET) {
int opt;
if (opcode < Opcodes.ISTORE) {
/* ILOAD_0 */
opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
} else {
/* ISTORE_0 */
opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
}
code.putByte(opt);
} else if (var >= 256) {
code.putByte(196 /* WIDE */).put12(opcode, var);
} else {
code.put11(opcode, var);
}
if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) {
visitLabel(new Label());
}
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
Item i = cw.newClassItem(type);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, code.length, cw, i);
} else if (opcode == Opcodes.NEW) {
// updates current and max stack sizes only if opcode == NEW
// (no stack change 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, i.index);
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
Item i = cw.newFieldItem(owner, name, desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
int size;
// computes the stack size variation
char c = desc.charAt(0);
switch (opcode) {
case Opcodes.GETSTATIC:
size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
break;
case Opcodes.PUTSTATIC:
size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
break;
case Opcodes.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, i.index);
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
boolean itf = opcode == Opcodes.INVOKEINTERFACE;
Item i = cw.newMethodItem(owner, name, desc, itf);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
/*
* 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 = Type.getArgumentsAndReturnSizes(desc);
// ... and we save them in order
// not to recompute them in the future
i.intVal = argSize;
}
int size;
if (opcode == Opcodes.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 (argSize == 0) {
argSize = Type.getArgumentsAndReturnSizes(desc);
i.intVal = argSize;
}
code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
} else {
code.put12(opcode, i.index);
}
}
@Override
public void visitInvokeDynamicInsn(final String name, final String desc,
final Handle bsm, final Object... bsmArgs) {
Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
} else {
/*
* 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 = Type.getArgumentsAndReturnSizes(desc);
// ... and we save them in order
// not to recompute them in the future
i.intVal = argSize;
}
int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
code.put12(Opcodes.INVOKEDYNAMIC, i.index);
code.putShort(0);
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
Label nextInsn = null;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
// 'label' is the target of a jump instruction
label.getFirst().status |= Label.TARGET;
// adds 'label' as a successor of this basic block
addSuccessor(Edge.NORMAL, label);
if (opcode != Opcodes.GOTO) {
// creates a Label for the next basic block
nextInsn = new Label();
}
} else {
if (opcode == Opcodes.JSR) {
if ((label.status & Label.SUBROUTINE) == 0) {
label.status |= Label.SUBROUTINE;
++subroutines;
}
currentBlock.status |= Label.JSR;
addSuccessor(stackSize + 1, label);
// creates a Label for the next basic block
nextInsn = new Label();
/*
* note that, by construction in this method, a JSR block
* has at least two successors in the control flow graph:
* the first one leads the next instruction after the JSR,
* while the second one leads to the JSR target.
*/
} else {
// updates current stack size (max stack size unchanged
// because stack size variation always negative in this
// case)
stackSize += Frame.SIZE[opcode];
addSuccessor(stackSize, label);
}
}
}
// adds the instruction to the bytecode of the method
if ((label.status & Label.RESOLVED) != 0
&& 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 == Opcodes.GOTO) {
code.putByte(200); // GOTO_W
} else if (opcode == Opcodes.JSR) {
code.putByte(201); // JSR_W
} else {
// if the IF instruction is transformed into IFNOT GOTO_W the
// next instruction becomes the target of the IFNOT instruction
if (nextInsn != null) {
nextInsn.status |= Label.TARGET;
}
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);
}
if (currentBlock != null) {
if (nextInsn != null) {
// if the jump instruction is not a GOTO, the next instruction
// is also a successor of this instruction. Calling visitLabel
// adds the label of this next instruction as a successor of the
// current block, and starts a new basic block
visitLabel(nextInsn);
}
if (opcode == Opcodes.GOTO) {
noSuccessor();
}
}
}
@Override
public void visitLabel(final Label label) {
// resolves previous forward references to label, if any
resize |= label.resolve(this, code.length, code.data);
// updates currentBlock
if ((label.status & Label.DEBUG) != 0) {
return;
}
if (compute == FRAMES) {
if (currentBlock != null) {
if (label.position == currentBlock.position) {
// successive labels, do not start a new basic block
currentBlock.status |= (label.status & Label.TARGET);
label.frame = currentBlock.frame;
return;
}
// ends current block (with one new successor)
addSuccessor(Edge.NORMAL, label);
}
// begins a new current block
currentBlock = label;
if (label.frame == null) {
label.frame = new Frame();
label.frame.owner = label;
}
// updates the basic block list
if (previousBlock != null) {
if (label.position == previousBlock.position) {
previousBlock.status |= (label.status & Label.TARGET);
label.frame = previousBlock.frame;
currentBlock = previousBlock;
return;
}
previousBlock.successor = label;
}
previousBlock = label;
} else if (compute == MAXS) {
if (currentBlock != null) {
// ends current block (with one new successor)
currentBlock.outputStackMax = maxStackSize;
addSuccessor(stackSize, label);
}
// begins a new current block
currentBlock = label;
// resets the relative current and max stack sizes
stackSize = 0;
maxStackSize = 0;
// updates the basic block list
if (previousBlock != null) {
previousBlock.successor = label;
}
previousBlock = label;
}
}
@Override
public void visitLdcInsn(final Object cst) {
Item i = cw.newConstItem(cst);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
} else {
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(Opcodes.LDC, index);
}
}
@Override
public void visitIincInsn(final int var, final int increment) {
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(Opcodes.IINC, var, null, null);
}
}
if (compute != NOTHING) {
// updates max locals
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(Opcodes.IINC, var)
.putShort(increment);
} else {
code.putByte(Opcodes.IINC).put11(var, increment);
}
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Opcodes.TABLESWITCH);
code.putByteArray(null, 0, (4 - code.length % 4) % 4);
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);
}
// updates currentBlock
visitSwitchInsn(dflt, labels);
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Opcodes.LOOKUPSWITCH);
code.putByteArray(null, 0, (4 - code.length % 4) % 4);
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);
}
// updates currentBlock
visitSwitchInsn(dflt, labels);
}
private void visitSwitchInsn(final Label dflt, final Label[] labels) {
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
// adds current block successors
addSuccessor(Edge.NORMAL, dflt);
dflt.getFirst().status |= Label.TARGET;
for (int i = 0; i < labels.length; ++i) {
addSuccessor(Edge.NORMAL, labels[i]);
labels[i].getFirst().status |= Label.TARGET;
}
} else {
// updates current stack size (max stack size unchanged)
--stackSize;
// adds current block successors
addSuccessor(stackSize, dflt);
for (int i = 0; i < labels.length; ++i) {
addSuccessor(stackSize, labels[i]);
}
}
// ends current block
noSuccessor();
}
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
Item i = cw.newClassItem(desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
} else {
// 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(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
}
@Override
public void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type) {
++handlerCount;
Handler h = new Handler();
h.start = start;
h.end = end;
h.handler = handler;
h.desc = type;
h.type = type != null ? cw.newClass(type) : 0;
if (lastHandler == null) {
firstHandler = h;
} else {
lastHandler.next = h;
}
lastHandler = h;
}
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
if (signature != null) {
if (localVarType == null) {
localVarType = new ByteVector();
}
++localVarTypeCount;
localVarType.putShort(start.position)
.putShort(end.position - start.position)
.putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature))
.putShort(index);
}
if (localVar == null) {
localVar = new ByteVector();
}
++localVarCount;
localVar.putShort(start.position)
.putShort(end.position - start.position)
.putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc))
.putShort(index);
if (compute != NOTHING) {
// updates max locals
char c = desc.charAt(0);
int n = index + (c == 'J' || c == 'D' ? 2 : 1);
if (n > maxLocals) {
maxLocals = n;
}
}
}
@Override
public void visitLineNumber(final int line, final Label start) {
if (lineNumber == null) {
lineNumber = new ByteVector();
}
++lineNumberCount;
lineNumber.putShort(start.position);
lineNumber.putShort(line);
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
if (ClassReader.FRAMES && compute == FRAMES) {
// completes the control flow graph with exception handler blocks
Handler handler = firstHandler;
while (handler != null) {
Label l = handler.start.getFirst();
Label h = handler.handler.getFirst();
Label e = handler.end.getFirst();
// computes the kind of the edges to 'h'
String t = handler.desc == null ? "java/lang/Throwable"
: handler.desc;
int kind = Frame.OBJECT | cw.addType(t);
// h is an exception handler
h.status |= Label.TARGET;
// adds 'h' as a successor of labels between 'start' and 'end'
while (l != e) {
// creates an edge to 'h'
Edge b = new Edge();
b.info = kind;
b.successor = h;
// adds it to the successors of 'l'
b.next = l.successors;
l.successors = b;
// goes to the next label
l = l.successor;
}
handler = handler.next;
}
// creates and visits the first (implicit) frame
Frame f = labels.frame;
Type[] args = Type.getArgumentTypes(descriptor);
f.initInputFrame(cw, access, args, this.maxLocals);
visitFrame(f);
/*
* fix point algorithm: mark the first basic block as 'changed'
* (i.e. put it in the 'changed' list) and, while there are changed
* basic blocks, choose one, mark it as unchanged, and update its
* successors (which can be changed in the process).
*/
int max = 0;
Label changed = labels;
while (changed != null) {
// removes a basic block from the list of changed basic blocks
Label l = changed;
changed = changed.next;
l.next = null;
f = l.frame;
// a reachable jump target must be stored in the stack map
if ((l.status & Label.TARGET) != 0) {
l.status |= Label.STORE;
}
// all visited labels are reachable, by definition
l.status |= Label.REACHABLE;
// updates the (absolute) maximum stack size
int blockMax = f.inputStack.length + l.outputStackMax;
if (blockMax > max) {
max = blockMax;
}
// updates the successors of the current basic block
Edge e = l.successors;
while (e != null) {
Label n = e.successor.getFirst();
boolean change = f.merge(cw, n.frame, e.info);
if (change && n.next == null) {
// if n has changed and is not already in the 'changed'
// list, adds it to this list
n.next = changed;
changed = n;
}
e = e.next;
}
}
// visits all the frames that must be stored in the stack map
Label l = labels;
while (l != null) {
f = l.frame;
if ((l.status & Label.STORE) != 0) {
visitFrame(f);
}
if ((l.status & Label.REACHABLE) == 0) {
// finds start and end of dead basic block
Label k = l.successor;
int start = l.position;
int end = (k == null ? code.length : k.position) - 1;
// if non empty basic block
if (end >= start) {
max = Math.max(max, 1);
// replaces instructions with NOP ... NOP ATHROW
for (int i = start; i < end; ++i) {
code.data[i] = Opcodes.NOP;
}
code.data[end] = (byte) Opcodes.ATHROW;
// emits a frame for this unreachable block
int frameIndex = startFrame(start, 0, 1);
frame[frameIndex] = Frame.OBJECT
| cw.addType("java/lang/Throwable");
endFrame();
// removes the start-end range from the exception
// handlers
firstHandler = Handler.remove(firstHandler, l, k);
}
}
l = l.successor;
}
handler = firstHandler;
handlerCount = 0;
while (handler != null) {
handlerCount += 1;
handler = handler.next;
}
this.maxStack = max;
} else if (compute == MAXS) {
// completes the control flow graph with exception handler blocks
Handler handler = firstHandler;
while (handler != null) {
Label l = handler.start;
Label h = handler.handler;
Label e = handler.end;
// adds 'h' as a successor of labels between 'start' and 'end'
while (l != e) {
// creates an edge to 'h'
Edge b = new Edge();
b.info = Edge.EXCEPTION;
b.successor = h;
// adds it to the successors of 'l'
if ((l.status & Label.JSR) == 0) {
b.next = l.successors;
l.successors = b;
} else {
// if l is a JSR block, adds b after the first two edges
// to preserve the hypothesis about JSR block successors
// order (see {@link #visitJumpInsn})
b.next = l.successors.next.next;
l.successors.next.next = b;
}
// goes to the next label
l = l.successor;
}
handler = handler.next;
}
if (subroutines > 0) {
// completes the control flow graph with the RET successors
/*
* first step: finds the subroutines. This step determines, for
* each basic block, to which subroutine(s) it belongs.
*/
// finds the basic blocks that belong to the "main" subroutine
int id = 0;
labels.visitSubroutine(null, 1, subroutines);
// finds the basic blocks that belong to the real subroutines
Label l = labels;
while (l != null) {
if ((l.status & Label.JSR) != 0) {
// the subroutine is defined by l's TARGET, not by l
Label subroutine = l.successors.next.successor;
// if this subroutine has not been visited yet...
if ((subroutine.status & Label.VISITED) == 0) {
// ...assigns it a new id and finds its basic blocks
id += 1;
subroutine.visitSubroutine(null, (id / 32L) << 32
| (1L << (id % 32)), subroutines);
}
}
l = l.successor;
}
// second step: finds the successors of RET blocks
l = labels;
while (l != null) {
if ((l.status & Label.JSR) != 0) {
Label L = labels;
while (L != null) {
L.status &= ~Label.VISITED2;
L = L.successor;
}
// the subroutine is defined by l's TARGET, not by l
Label subroutine = l.successors.next.successor;
subroutine.visitSubroutine(l, 0, subroutines);
}
l = l.successor;
}
}
/*
* 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#inputStackTop} of the
* blocks in the block stack are the true (non relative) beginning
* stack sizes of these blocks.
*/
int max = 0;
Label stack = labels;
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.inputStackTop;
int blockMax = start + l.outputStackMax;
// updates the global max stack size
if (blockMax > max) {
max = blockMax;
}
// analyzes the successors of the block
Edge b = l.successors;
if ((l.status & Label.JSR) != 0) {
// ignores the first edge of JSR blocks (virtual successor)
b = b.next;
}
while (b != null) {
l = b.successor;
// if this successor has not already been pushed...
if ((l.status & Label.PUSHED) == 0) {
// computes its true beginning stack size...
l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start
+ b.info;
// ...and pushes it onto the stack
l.status |= Label.PUSHED;
l.next = stack;
stack = l;
}
b = b.next;
}
}
this.maxStack = Math.max(maxStack, max);
} else {
this.maxStack = maxStack;
this.maxLocals = maxLocals;
}
}
@Override
public void visitEnd() {
}
// ------------------------------------------------------------------------
// Utility methods: control flow analysis algorithm
// ------------------------------------------------------------------------
/**
* Adds a successor to the {@link #currentBlock currentBlock} block.
*
* @param info
* information about the control flow edge to be added.
* @param successor
* the successor block to be added to the current block.
*/
private void addSuccessor(final int info, final Label successor) {
// creates and initializes an Edge object...
Edge b = new Edge();
b.info = info;
b.successor = successor;
// ...and adds it to the successor list of the currentBlock block
b.next = currentBlock.successors;
currentBlock.successors = b;
}
/**
* Ends the current basic block. This method must be used in the case where
* the current basic block does not have any successor.
*/
private void noSuccessor() {
if (compute == FRAMES) {
Label l = new Label();
l.frame = new Frame();
l.frame.owner = l;
l.resolve(this, code.length, code.data);
previousBlock.successor = l;
previousBlock = l;
} else {
currentBlock.outputStackMax = maxStackSize;
}
currentBlock = null;
}
// ------------------------------------------------------------------------
// Utility methods: stack map frames
// ------------------------------------------------------------------------
/**
* Visits a frame that has been computed from scratch.
*
* @param f
* the frame that must be visited.
*/
private void visitFrame(final Frame f) {
int i, t;
int nTop = 0;
int nLocal = 0;
int nStack = 0;
int[] locals = f.inputLocals;
int[] stacks = f.inputStack;
// computes the number of locals (ignores TOP types that are just after
// a LONG or a DOUBLE, and all trailing TOP types)
for (i = 0; i < locals.length; ++i) {
t = locals[i];
if (t == Frame.TOP) {
++nTop;
} else {
nLocal += nTop + 1;
nTop = 0;
}
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
// computes the stack size (ignores TOP types that are just after
// a LONG or a DOUBLE)
for (i = 0; i < stacks.length; ++i) {
t = stacks[i];
++nStack;
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
// visits the frame and its content
int frameIndex = startFrame(f.owner.position, nLocal, nStack);
for (i = 0; nLocal > 0; ++i, --nLocal) {
t = locals[i];
frame[frameIndex++] = t;
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
for (i = 0; i < stacks.length; ++i) {
t = stacks[i];
frame[frameIndex++] = t;
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
endFrame();
}
/**
* Visit the implicit first frame of this method.
*/
private void visitImplicitFirstFrame() {
// There can be at most descriptor.length() + 1 locals
int frameIndex = startFrame(0, descriptor.length() + 1, 0);
if ((access & Opcodes.ACC_STATIC) == 0) {
if ((access & ACC_CONSTRUCTOR) == 0) {
frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName);
} else {
frame[frameIndex++] = 6; // Opcodes.UNINITIALIZED_THIS;
}
}
int i = 1;
loop: while (true) {
int j = i;
switch (descriptor.charAt(i++)) {
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
frame[frameIndex++] = 1; // Opcodes.INTEGER;
break;
case 'F':
frame[frameIndex++] = 2; // Opcodes.FLOAT;
break;
case 'J':
frame[frameIndex++] = 4; // Opcodes.LONG;
break;
case 'D':
frame[frameIndex++] = 3; // Opcodes.DOUBLE;
break;
case '[':
while (descriptor.charAt(i) == '[') {
++i;
}
if (descriptor.charAt(i) == 'L') {
++i;
while (descriptor.charAt(i) != ';') {
++i;
}
}
frame[frameIndex++] = Frame.OBJECT
| cw.addType(descriptor.substring(j, ++i));
break;
case 'L':
while (descriptor.charAt(i) != ';') {
++i;
}
frame[frameIndex++] = Frame.OBJECT
| cw.addType(descriptor.substring(j + 1, i++));
break;
default:
break loop;
}
}
frame[1] = frameIndex - 3;
endFrame();
}
/**
* Starts the visit of a stack map frame.
*
* @param offset
* the offset of the instruction to which the frame corresponds.
* @param nLocal
* the number of local variables in the frame.
* @param nStack
* the number of stack elements in the frame.
* @return the index of the next element to be written in this frame.
*/
private int startFrame(final int offset, final int nLocal, final int nStack) {
int n = 3 + nLocal + nStack;
if (frame == null || frame.length < n) {
frame = new int[n];
}
frame[0] = offset;
frame[1] = nLocal;
frame[2] = nStack;
return 3;
}
/**
* Checks if the visit of the current frame {@link #frame} is finished, and
* if yes, write it in the StackMapTable attribute.
*/
private void endFrame() {
if (previousFrame != null) { // do not write the first frame
if (stackMap == null) {
stackMap = new ByteVector();
}
writeFrame();
++frameCount;
}
previousFrame = frame;
frame = null;
}
/**
* Compress and writes the current frame {@link #frame} in the StackMapTable
* attribute.
*/
private void writeFrame() {
int clocalsSize = frame[1];
int cstackSize = frame[2];
if ((cw.version & 0xFFFF) < Opcodes.V1_6) {
stackMap.putShort(frame[0]).putShort(clocalsSize);
writeFrameTypes(3, 3 + clocalsSize);
stackMap.putShort(cstackSize);
writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
return;
}
int localsSize = previousFrame[1];
int type = FULL_FRAME;
int k = 0;
int delta;
if (frameCount == 0) {
delta = frame[0];
} else {
delta = frame[0] - previousFrame[0] - 1;
}
if (cstackSize == 0) {
k = clocalsSize - localsSize;
switch (k) {
case -3:
case -2:
case -1:
type = CHOP_FRAME;
localsSize = clocalsSize;
break;
case 0:
type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
break;
case 1:
case 2:
case 3:
type = APPEND_FRAME;
break;
}
} else if (clocalsSize == localsSize && cstackSize == 1) {
type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME
: SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
}
if (type != FULL_FRAME) {
// verify if locals are the same
int l = 3;
for (int j = 0; j < localsSize; j++) {
if (frame[l] != previousFrame[l]) {
type = FULL_FRAME;
break;
}
l++;
}
}
switch (type) {
case SAME_FRAME:
stackMap.putByte(delta);
break;
case SAME_LOCALS_1_STACK_ITEM_FRAME:
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
break;
case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(
delta);
writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
break;
case SAME_FRAME_EXTENDED:
stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
break;
case CHOP_FRAME:
stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
break;
case APPEND_FRAME:
stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
writeFrameTypes(3 + localsSize, 3 + clocalsSize);
break;
// case FULL_FRAME:
default:
stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize);
writeFrameTypes(3, 3 + clocalsSize);
stackMap.putShort(cstackSize);
writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
}
}
/**
* Writes some types of the current frame {@link #frame} into the
* StackMapTableAttribute. This method converts types from the format used
* in {@link Label} to the format used in StackMapTable attributes. In
* particular, it converts type table indexes to constant pool indexes.
*
* @param start
* index of the first type in {@link #frame} to write.
* @param end
* index of last type in {@link #frame} to write (exclusive).
*/
private void writeFrameTypes(final int start, final int end) {
for (int i = start; i < end; ++i) {
int t = frame[i];
int d = t & Frame.DIM;
if (d == 0) {
int v = t & Frame.BASE_VALUE;
switch (t & Frame.BASE_KIND) {
case Frame.OBJECT:
stackMap.putByte(7).putShort(
cw.newClass(cw.typeTable[v].strVal1));
break;
case Frame.UNINITIALIZED:
stackMap.putByte(8).putShort(cw.typeTable[v].intVal);
break;
default:
stackMap.putByte(v);
}
} else {
StringBuffer buf = new StringBuffer();
d >>= 28;
while (d-- > 0) {
buf.append('[');
}
if ((t & Frame.BASE_KIND) == Frame.OBJECT) {
buf.append('L');
buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
buf.append(';');
} else {
switch (t & 0xF) {
case 1:
buf.append('I');
break;
case 2:
buf.append('F');
break;
case 3:
buf.append('D');
break;
case 9:
buf.append('Z');
break;
case 10:
buf.append('B');
break;
case 11:
buf.append('C');
break;
case 12:
buf.append('S');
break;
default:
buf.append('J');
}
}
stackMap.putByte(7).putShort(cw.newClass(buf.toString()));
}
}
}
private void writeFrameType(final Object type) {
if (type instanceof String) {
stackMap.putByte(7).putShort(cw.newClass((String) type));
} else if (type instanceof Integer) {
stackMap.putByte(((Integer) type).intValue());
} else {
stackMap.putByte(8).putShort(((Label) type).position);
}
}
// ------------------------------------------------------------------------
// 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 (classReaderOffset != 0) {
return 6 + classReaderLength;
}
if (resize) {
// replaces the temporary jump opcodes introduced by Label.resolve.
if (ClassReader.RESIZE) {
resizeInstructions();
} else {
throw new RuntimeException("Method code too large!");
}
}
int size = 8;
if (code.length > 0) {
if (code.length > 65536) {
throw new RuntimeException("Method code too large!");
}
cw.newUTF8("Code");
size += 18 + code.length + 8 * handlerCount;
if (localVar != null) {
cw.newUTF8("LocalVariableTable");
size += 8 + localVar.length;
}
if (localVarType != null) {
cw.newUTF8("LocalVariableTypeTable");
size += 8 + localVarType.length;
}
if (lineNumber != null) {
cw.newUTF8("LineNumberTable");
size += 8 + lineNumber.length;
}
if (stackMap != null) {
boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
cw.newUTF8(zip ? "StackMapTable" : "StackMap");
size += 8 + stackMap.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 & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
cw.newUTF8("Synthetic");
size += 6;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
cw.newUTF8("Deprecated");
size += 6;
}
if (ClassReader.SIGNATURES && signature != null) {
cw.newUTF8("Signature");
cw.newUTF8(signature);
size += 8;
}
if (ClassReader.ANNOTATIONS && annd != null) {
cw.newUTF8("AnnotationDefault");
size += 6 + annd.length;
}
if (ClassReader.ANNOTATIONS && anns != null) {
cw.newUTF8("RuntimeVisibleAnnotations");
size += 8 + anns.getSize();
}
if (ClassReader.ANNOTATIONS && ianns != null) {
cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize();
}
if (ClassReader.ANNOTATIONS && panns != null) {
cw.newUTF8("RuntimeVisibleParameterAnnotations");
size += 7 + 2 * (panns.length - synthetics);
for (int i = panns.length - 1; i >= synthetics; --i) {
size += panns[i] == null ? 0 : panns[i].getSize();
}
}
if (ClassReader.ANNOTATIONS && ipanns != null) {
cw.newUTF8("RuntimeInvisibleParameterAnnotations");
size += 7 + 2 * (ipanns.length - synthetics);
for (int i = ipanns.length - 1; i >= synthetics; --i) {
size += ipanns[i] == null ? 0 : ipanns[i].getSize();
}
}
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) {
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
out.putShort(access & ~mask).putShort(name).putShort(desc);
if (classReaderOffset != 0) {
out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);
return;
}
int attributeCount = 0;
if (code.length > 0) {
++attributeCount;
}
if (exceptionCount > 0) {
++attributeCount;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
++attributeCount;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
++attributeCount;
}
if (ClassReader.SIGNATURES && signature != null) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && annd != null) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && anns != null) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && ianns != null) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && panns != null) {
++attributeCount;
}
if (ClassReader.ANNOTATIONS && ipanns != null) {
++attributeCount;
}
if (attrs != null) {
attributeCount += attrs.getCount();
}
out.putShort(attributeCount);
if (code.length > 0) {
int size = 12 + code.length + 8 * handlerCount;
if (localVar != null) {
size += 8 + localVar.length;
}
if (localVarType != null) {
size += 8 + localVarType.length;
}
if (lineNumber != null) {
size += 8 + lineNumber.length;
}
if (stackMap != null) {
size += 8 + stackMap.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(handlerCount);
if (handlerCount > 0) {
Handler h = firstHandler;
while (h != null) {
out.putShort(h.start.position).putShort(h.end.position)
.putShort(h.handler.position).putShort(h.type);
h = h.next;
}
}
attributeCount = 0;
if (localVar != null) {
++attributeCount;
}
if (localVarType != null) {
++attributeCount;
}
if (lineNumber != null) {
++attributeCount;
}
if (stackMap != 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 (localVarType != null) {
out.putShort(cw.newUTF8("LocalVariableTypeTable"));
out.putInt(localVarType.length + 2).putShort(localVarTypeCount);
out.putByteArray(localVarType.data, 0, localVarType.length);
}
if (lineNumber != null) {
out.putShort(cw.newUTF8("LineNumberTable"));
out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
out.putByteArray(lineNumber.data, 0, lineNumber.length);
}
if (stackMap != null) {
boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap"));
out.putInt(stackMap.length + 2).putShort(frameCount);
out.putByteArray(stackMap.data, 0, stackMap.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 & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
out.putShort(cw.newUTF8("Synthetic")).putInt(0);
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
}
if (ClassReader.SIGNATURES && signature != null) {
out.putShort(cw.newUTF8("Signature")).putInt(2)
.putShort(cw.newUTF8(signature));
}
if (ClassReader.ANNOTATIONS && annd != null) {
out.putShort(cw.newUTF8("AnnotationDefault"));
out.putInt(annd.length);
out.putByteArray(annd.data, 0, annd.length);
}
if (ClassReader.ANNOTATIONS && anns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
anns.put(out);
}
if (ClassReader.ANNOTATIONS && ianns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
if (ClassReader.ANNOTATIONS && panns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
AnnotationWriter.put(panns, synthetics, out);
}
if (ClassReader.ANNOTATIONS && ipanns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
AnnotationWriter.put(ipanns, synthetics, out);
}
if (attrs != null) {
attrs.put(cw, null, 0, -1, -1, out);
}
}
// ------------------------------------------------------------------------
// Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
// ------------------------------------------------------------------------
/**
* Resizes and replaces the temporary instructions inserted by
* {@link Label#resolve} for wide forward jumps, 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.
*/
private void resizeInstructions() {
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[0]; // copy of indexes
int[] allSizes = new int[0]; // copy of sizes
boolean[] resize; // instructions to be resized
int newOffset; // future offset of a jump instruction
resize = new boolean[code.length];
// 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
int state = 3;
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, 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 == Opcodes.GOTO || opcode == Opcodes.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 == Opcodes.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:
case ClassWriter.INDYMETH_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) {
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 == Opcodes.GOTO) {
newCode.putByte(200); // GOTO_W
} else if (opcode == Opcodes.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 now computed from start of GOTO_W
newOffset -= 3;
}
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
newCode.putByte(Opcodes.TABLESWITCH);
newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
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
newCode.putByte(Opcodes.LOOKUPSWITCH);
newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
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 == Opcodes.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:
case ClassWriter.INDYMETH_INSN:
newCode.putByteArray(b, u, 5);
u += 5;
break;
// case MANA_INSN:
default:
newCode.putByteArray(b, u, 4);
u += 4;
break;
}
}
// recomputes the stack map frames
if (frameCount > 0) {
if (compute == FRAMES) {
frameCount = 0;
stackMap = null;
previousFrame = null;
frame = null;
Frame f = new Frame();
f.owner = labels;
Type[] args = Type.getArgumentTypes(descriptor);
f.initInputFrame(cw, access, args, maxLocals);
visitFrame(f);
Label l = labels;
while (l != null) {
/*
* here we need the original label position. getNewOffset
* must therefore never have been called for this label.
*/
u = l.position - 3;
if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) {
getNewOffset(allIndexes, allSizes, l);
// TODO update offsets in UNINITIALIZED values
visitFrame(l.frame);
}
l = l.successor;
}
} else {
/*
* Resizing an existing stack map frame table is really hard.
* Not only the table must be parsed to update the offets, but
* new frames may be needed for jump instructions that were
* inserted by this method. And updating the offsets or
* inserting frames can change the format of the following
* frames, in case of packed frames. In practice the whole table
* must be recomputed. For this the frames are marked as
* potentially invalid. This will cause the whole class to be
* reread and rewritten with the COMPUTE_FRAMES option (see the
* ClassWriter.toByteArray method). This is not very efficient
* but is much easier and requires much less code than any other
* method I can think of.
*/
cw.invalidFrames = true;
}
}
// updates the exception handler block labels
Handler h = firstHandler;
while (h != null) {
getNewOffset(allIndexes, allSizes, h.start);
getNewOffset(allIndexes, allSizes, h.end);
getNewOffset(allIndexes, allSizes, h.handler);
h = h.next;
}
// updates the instructions addresses in the
// local var and line number tables
for (i = 0; i < 2; ++i) {
ByteVector bv = i == 0 ? localVar : localVarType;
if (bv != null) {
b = bv.data;
u = 0;
while (u < bv.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 + 2, 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
Attribute attr = cattrs;
while (attr != null) {
Label[] labels = attr.getLabels();
if (labels != null) {
for (i = labels.length - 1; i >= 0; --i) {
getNewOffset(allIndexes, allSizes, labels[i]);
}
}
attr = attr.next;
}
// replaces old bytecodes with new ones
code = newCode;
}
/**
* 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;
}
/**
* Updates the offset of the given label.
*
* @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 label
* the label whose offset must be updated.
*/
static void getNewOffset(final int[] indexes, final int[] sizes,
final Label label) {
if ((label.status & Label.RESIZED) == 0) {
label.position = getNewOffset(indexes, sizes, 0, label.position);
label.status |= Label.RESIZED;
}
}
}
================================================
FILE: src/jvm/clojure/asm/Opcodes.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.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.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public interface Opcodes {
// ASM API versions
int ASM4 = 4 << 16 | 0 << 8 | 0;
// versions
int V1_1 = 3 << 16 | 45;
int V1_2 = 0 << 16 | 46;
int V1_3 = 0 << 16 | 47;
int V1_4 = 0 << 16 | 48;
int V1_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51;
// access flags
int ACC_PUBLIC = 0x0001; // class, field, method
int ACC_PRIVATE = 0x0002; // class, field, method
int ACC_PROTECTED = 0x0004; // class, field, method
int ACC_STATIC = 0x0008; // field, method
int ACC_FINAL = 0x0010; // class, field, method
int ACC_SUPER = 0x0020; // class
int ACC_SYNCHRONIZED = 0x0020; // method
int ACC_VOLATILE = 0x0040; // field
int ACC_BRIDGE = 0x0040; // method
int ACC_VARARGS = 0x0080; // method
int ACC_TRANSIENT = 0x0080; // field
int ACC_NATIVE = 0x0100; // method
int ACC_INTERFACE = 0x0200; // class
int ACC_ABSTRACT = 0x0400; // class, method
int ACC_STRICT = 0x0800; // method
int ACC_SYNTHETIC = 0x1000; // class, field, method
int ACC_ANNOTATION = 0x2000; // class
int ACC_ENUM = 0x4000; // class(?) field inner
// ASM specific pseudo access flags
int ACC_DEPRECATED = 0x20000; // class, field, method
// 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;
// tags for Handle
int H_GETFIELD = 1;
int H_GETSTATIC = 2;
int H_PUTFIELD = 3;
int H_PUTSTATIC = 4;
int H_INVOKEVIRTUAL = 5;
int H_INVOKESTATIC = 6;
int H_INVOKESPECIAL = 7;
int H_NEWINVOKESPECIAL = 8;
int H_INVOKEINTERFACE = 9;
// stack map frame types
/**
* Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}.
*/
int F_NEW = -1;
/**
* Represents a compressed frame with complete frame data.
*/
int F_FULL = 0;
/**
* Represents a compressed frame where locals are the same as the locals in
* the previous frame, except that additional 1-3 locals are defined, and
* with an empty stack.
*/
int F_APPEND = 1;
/**
* Represents a compressed frame where locals are the same as the locals in
* the previous frame, except that the last 1-3 locals are absent and with
* an empty stack.
*/
int F_CHOP = 2;
/**
* Represents a compressed frame with exactly the same locals as the
* previous frame and with an empty stack.
*/
int F_SAME = 3;
/**
* Represents a compressed frame with exactly the same locals as the
* previous frame and with a single value on the stack.
*/
int F_SAME1 = 4;
Integer TOP = new Integer(0);
Integer INTEGER = new Integer(1);
Integer FLOAT = new Integer(2);
Integer DOUBLE = new Integer(3);
Integer LONG = new Integer(4);
Integer NULL = new Integer(5);
Integer UNINITIALIZED_THIS = new Integer(6);
// 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 INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn
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; // -
}
================================================
FILE: src/jvm/clojure/asm/Type.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* A Java field or method type. This class can be used to make it easier to
* manipulate type and method descriptors.
*
* @author Eric Bruneton
* @author Chris Nokleberg
*/
public class Type {
/**
* The sort of the void type. See {@link #getSort getSort}.
*/
public static final int VOID = 0;
/**
* The sort of the boolean type. See {@link #getSort getSort}.
*/
public static final int BOOLEAN = 1;
/**
* The sort of the char type. See {@link #getSort getSort}.
*/
public static final int CHAR = 2;
/**
* The sort of the byte type. See {@link #getSort getSort}.
*/
public static final int BYTE = 3;
/**
* The sort of the short type. See {@link #getSort getSort}.
*/
public static final int SHORT = 4;
/**
* The sort of the int type. See {@link #getSort getSort}.
*/
public static final int INT = 5;
/**
* The sort of the float type. See {@link #getSort getSort}.
*/
public static final int FLOAT = 6;
/**
* The sort of the long type. See {@link #getSort getSort}.
*/
public static final int LONG = 7;
/**
* The sort of the double type. See {@link #getSort getSort}.
*/
public static final int DOUBLE = 8;
/**
* The sort of array reference types. See {@link #getSort getSort}.
*/
public static final int ARRAY = 9;
/**
* The sort of object reference types. See {@link #getSort getSort}.
*/
public static final int OBJECT = 10;
/**
* The sort of method types. See {@link #getSort getSort}.
*/
public static final int METHOD = 11;
/**
* The void type.
*/
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1);
/**
* The boolean type.
*/
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The char type.
*/
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1);
/**
* The byte type.
*/
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The short type.
*/
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1);
/**
* The int type.
*/
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1);
/**
* The float type.
*/
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1);
/**
* The long type.
*/
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1);
/**
* The double type.
*/
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1);
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
/**
* The sort of this Java type.
*/
private final int sort;
/**
* A buffer containing the internal name of this Java type. This field is
* only used for reference types.
*/
private final char[] buf;
/**
* The offset of the internal name of this Java type in {@link #buf buf} or,
* for primitive types, the size, descriptor and getOpcode offsets for this
* type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset
* for IALOAD or IASTORE, byte 3 the offset for all other instructions).
*/
private final int off;
/**
* The length of the internal name of this Java type.
*/
private final int len;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* 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 field or method 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 internal name.
*
* @param internalName
* an internal name.
* @return the Java type corresponding to the given internal name.
*/
public static Type getObjectType(final String internalName) {
char[] buf = internalName.toCharArray();
return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length);
}
/**
* Returns the Java type corresponding to the given method descriptor.
* Equivalent to Type.getType(methodDescriptor).
*
* @param methodDescriptor
* a method descriptor.
* @return the Java type corresponding to the given method descriptor.
*/
public static Type getMethodType(final String methodDescriptor) {
return getType(methodDescriptor.toCharArray(), 0);
}
/**
* Returns the Java method type 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 Java type corresponding to the given argument and return
* types.
*/
public static Type getMethodType(final Type returnType,
final Type... argumentTypes) {
return getType(getMethodDescriptor(returnType, argumentTypes));
}
/**
* 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 method type corresponding to the given constructor.
*
* @param c
* a {@link Constructor Constructor} object.
* @return the Java method type corresponding to the given constructor.
*/
public static Type getType(final Constructor> c) {
return getType(getConstructorDescriptor(c));
}
/**
* Returns the Java method type corresponding to the given method.
*
* @param m
* a {@link Method Method} object.
* @return the Java method type corresponding to the given method.
*/
public static Type getType(final Method m) {
return getType(getMethodDescriptor(m));
}
/**
* 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 + (args[size].sort == OBJECT ? 2 : 0);
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());
}
/**
* 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).
*/
public 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;
}
}
}
/**
* Returns the Java type corresponding to the given type descriptor. For
* method descriptors, buf is supposed to contain nothing more than the
* descriptor itself.
*
* @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':
len = 1;
while (buf[off + len] != ';') {
++len;
}
return new Type(OBJECT, buf, off + 1, len - 1);
// case '(':
default:
return new Type(METHOD, buf, off, buf.length - off);
}
}
// ------------------------------------------------------------------------
// 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}, {@link #OBJECT OBJECT} or {@link #METHOD
* METHOD}.
*/
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 binary name of the class corresponding to this type. This
* method must not be used on method types.
*
* @return the binary name of the class corresponding to this type.
*/
public String getClassName() {
switch (sort) {
case VOID:
return "void";
case BOOLEAN:
return "boolean";
case CHAR:
return "char";
case BYTE:
return "byte";
case SHORT:
return "short";
case INT:
return "int";
case FLOAT:
return "float";
case LONG:
return "long";
case DOUBLE:
return "double";
case ARRAY:
StringBuffer b = new StringBuffer(getElementType().getClassName());
for (int i = getDimensions(); i > 0; --i) {
b.append("[]");
}
return b.toString();
case OBJECT:
return new String(buf, off, len).replace('/', '.');
default:
return null;
}
}
/**
* Returns the internal name of the class corresponding to this object or
* array type. The internal name of a class is its fully qualified name (as
* returned by Class.getName(), where '.' are replaced by '/'. This method
* should only be used for an object or array type.
*
* @return the internal name of the class corresponding to this object type.
*/
public String getInternalName() {
return new String(buf, off, len);
}
/**
* Returns the argument types of methods of this type. This method should
* only be used for method types.
*
* @return the argument types of methods of this type.
*/
public Type[] getArgumentTypes() {
return getArgumentTypes(getDescriptor());
}
/**
* Returns the return type of methods of this type. This method should only
* be used for method types.
*
* @return the return type of methods of this type.
*/
public Type getReturnType() {
return getReturnType(getDescriptor());
}
/**
* Returns the size of the arguments and of the return value of methods of
* this type. This method should only be used for method types.
*
* @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize,
* packed into a single int i = (argSize << 2) | retSize
* (argSize is therefore equal to i >> 2, and retSize to
* i & 0x03).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
}
// ------------------------------------------------------------------------
// 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) {
if (this.buf == null) {
// descriptor is in byte 3 of 'off' for primitive types (buf ==
// null)
buf.append((char) ((off & 0xFF000000) >>> 24));
} else if (sort == OBJECT) {
buf.append('L');
buf.append(this.buf, off, len);
buf.append(';');
} else { // sort == ARRAY || sort == METHOD
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, as returned by Class.getName(), where
* '.' are replaced by '/'.
*
* @param c
* an object or array 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 constructor.
*
* @param c
* a {@link Constructor Constructor} object.
* @return the descriptor of the given constructor.
*/
public static String getConstructorDescriptor(final Constructor> c) {
Class>[] parameters = c.getParameterTypes();
StringBuffer buf = new StringBuffer();
buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
getDescriptor(buf, parameters[i]);
}
return buf.append(")V").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. This method must not be used for
* method types.
*
* @return the size of values of this type, i.e., 2 for long and
* double, 0 for void and 1 otherwise.
*/
public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null)
return buf == null ? (off & 0xFF) : 1;
}
/**
* Returns a JVM instruction opcode adapted to this Java type. This method
* must not be used for method types.
*
* @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 == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
// the offset for IALOAD or IASTORE is in byte 1 of 'off' for
// primitive types (buf == null)
return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4);
} else {
// the offset for other instructions is in byte 2 of 'off' for
// primitive types (buf == null)
return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 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.
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Type)) {
return false;
}
Type t = (Type) o;
if (sort != t.sort) {
return false;
}
if (sort >= 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.
*/
@Override
public int hashCode() {
int hc = 13 * sort;
if (sort >= 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.
*/
@Override
public String toString() {
return getDescriptor();
}
}
================================================
FILE: src/jvm/clojure/asm/commons/AdviceAdapter.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2011 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.
*/
package clojure.asm.commons;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import clojure.asm.Handle;
import clojure.asm.Label;
import clojure.asm.MethodVisitor;
import clojure.asm.Opcodes;
import clojure.asm.Type;
/**
* A {@link clojure.asm.MethodVisitor} to insert before, after and around
* advices in methods and constructors.
*
* The behavior for constructors is like this:
*
*
*
as long as the INVOKESPECIAL for the object initialization has not been
* reached, every bytecode instruction is dispatched in the ctor code visitor
*
*
when this one is reached, it is only added in the ctor code visitor and a
* JP invoke is added
*
*
after that, only the other code visitor receives the instructions
*
*
*
* @author Eugene Kuleshov
* @author Eric Bruneton
*/
public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes {
private static final Object THIS = new Object();
private static final Object OTHER = new Object();
protected int methodAccess;
protected String methodDesc;
private boolean constructor;
private boolean superInitialized;
private List
Functions in clojure.core are automatically loaded. Other
namespaces can be loaded via require:
================================================
FILE: src/jvm/clojure/lang/AFn.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 4:05:37 PM */
package clojure.lang;
public abstract class AFn implements IFn {
public Object call() {
return invoke();
}
public void run(){
invoke();
}
public Object invoke() {
return throwArity(0);
}
public Object invoke(Object arg1) {
return throwArity(1);
}
public Object invoke(Object arg1, Object arg2) {
return throwArity(2);
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
return throwArity(3);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
return throwArity(4);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
return throwArity(5);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
return throwArity(6);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
{
return throwArity(7);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8) {
return throwArity(8);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9) {
return throwArity(9);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10) {
return throwArity(10);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11) {
return throwArity(11);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) {
return throwArity(12);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
{
return throwArity(13);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
{
return throwArity(14);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) {
return throwArity(15);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) {
return throwArity(16);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) {
return throwArity(17);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) {
return throwArity(18);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
return throwArity(19);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
{
return throwArity(20);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
Object... args)
{
return throwArity(21);
}
public Object applyTo(ISeq arglist) {
return applyToHelper(this, Util.ret1(arglist,arglist = null));
}
static public Object applyToHelper(IFn ifn, ISeq arglist) {
switch(RT.boundedLength(arglist, 20))
{
case 0:
arglist = null;
return ifn.invoke();
case 1:
return ifn.invoke(Util.ret1(arglist.first(),arglist = null));
case 2:
return ifn.invoke(arglist.first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 3:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 4:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 5:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 6:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 7:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 8:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 9:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 10:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 11:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 12:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 13:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 14:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 15:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 16:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 17:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 18:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 19:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 20:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
default:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, RT.seqToArray(Util.ret1(arglist.next(),arglist = null)));
}
}
public Object throwArity(int n){
String name = getClass().getSimpleName();
throw new ArityException(n, Compiler.demunge(name));
}
}
================================================
FILE: src/jvm/clojure/lang/AFunction.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 16, 2008 */
package clojure.lang;
import java.io.Serializable;
import java.util.Comparator;
public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {
public volatile MethodImplCache __methodImplCache;
public IPersistentMap meta(){
return null;
}
public IObj withMeta(final IPersistentMap meta){
return new RestFnWithMeta(this, meta);
}
public int compare(Object o1, Object o2){
Object o = invoke(o1, o2);
if(o instanceof Boolean)
{
if(RT.booleanCast(o))
return -1;
return RT.booleanCast(invoke(o2,o1))? 1 : 0;
}
Number n = (Number) o;
return n.intValue();
}
@Override
public boolean equals(Object f) {
return this == f;
}
}
================================================
FILE: src/jvm/clojure/lang/AMapEntry.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 1, 2008 */
package clojure.lang;
import java.io.StringWriter;
public abstract class AMapEntry extends APersistentVector implements IMapEntry{
public Object nth(int i){
if(i == 0)
return key();
else if(i == 1)
return val();
else
throw new IndexOutOfBoundsException();
}
private IPersistentVector asVector(){
return LazilyPersistentVector.createOwning(key(), val());
}
public IPersistentVector assocN(int i, Object val){
return asVector().assocN(i, val);
}
public int count(){
return 2;
}
public ISeq seq(){
return asVector().seq();
}
public IPersistentVector cons(Object o){
return asVector().cons(o);
}
public IPersistentCollection empty(){
return null;
}
public IPersistentStack pop(){
return LazilyPersistentVector.createOwning(key());
}
public Object setValue(Object value){
throw new UnsupportedOperationException();
}
/*
public boolean equals(Object obj){
return APersistentVector.doEquals(this, obj);
}
public int hashCode(){
//must match logic in APersistentVector
return 31 * (31 + Util.hash(key())) + Util.hash(val());
// return Util.hashCombine(Util.hashCombine(0, Util.hash(key())), Util.hash(val()));
}
public String toString(){
StringWriter sw = new StringWriter();
try
{
RT.print(this, sw);
}
catch(Exception e)
{
//checked exceptions stink!
throw Util.sneakyThrow(e);
}
return sw.toString();
}
public int length(){
return 2;
}
public Object nth(int i){
if(i == 0)
return key();
else if(i == 1)
return val();
else
throw new IndexOutOfBoundsException();
}
private IPersistentVector asVector(){
return LazilyPersistentVector.createOwning(key(), val());
}
public IPersistentVector assocN(int i, Object val){
return asVector().assocN(i, val);
}
public int count(){
return 2;
}
public ISeq seq(){
return asVector().seq();
}
public IPersistentVector cons(Object o){
return asVector().cons(o);
}
public boolean containsKey(Object key){
return asVector().containsKey(key);
}
public IMapEntry entryAt(Object key){
return asVector().entryAt(key);
}
public Associative assoc(Object key, Object val){
return asVector().assoc(key, val);
}
public Object valAt(Object key){
return asVector().valAt(key);
}
public Object valAt(Object key, Object notFound){
return asVector().valAt(key, notFound);
}
public Object peek(){
return val();
}
public ISeq rseq() {
return asVector().rseq();
}
*/
}
================================================
FILE: src/jvm/clojure/lang/APersistentMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.*;
public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence, IHashEq {
int _hash = -1;
int _hasheq = -1;
public String toString(){
return RT.printString(this);
}
public IPersistentCollection cons(Object o){
if(o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
return assoc(e.getKey(), e.getValue());
}
else if(o instanceof IPersistentVector)
{
IPersistentVector v = (IPersistentVector) o;
if(v.count() != 2)
throw new IllegalArgumentException("Vector arg to map conj must be a pair");
return assoc(v.nth(0), v.nth(1));
}
IPersistentMap ret = this;
for(ISeq es = RT.seq(o); es != null; es = es.next())
{
Map.Entry e = (Map.Entry) es.first();
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret;
}
public boolean equals(Object obj){
return mapEquals(this, obj);
}
static public boolean mapEquals(IPersistentMap m1, Object obj){
if(m1 == obj) return true;
if(!(obj instanceof Map))
return false;
Map m = (Map) obj;
if(m.size() != m1.count())
return false;
for(ISeq s = m1.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
boolean found = m.containsKey(e.getKey());
if(!found || !Util.equals(e.getValue(), m.get(e.getKey())))
return false;
}
return true;
}
public boolean equiv(Object obj){
if(!(obj instanceof Map))
return false;
if(obj instanceof IPersistentMap && !(obj instanceof MapEquivalence))
return false;
Map m = (Map) obj;
if(m.size() != size())
return false;
for(ISeq s = seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
boolean found = m.containsKey(e.getKey());
if(!found || !Util.equiv(e.getValue(), m.get(e.getKey())))
return false;
}
return true;
}
public int hashCode(){
if(_hash == -1)
{
this._hash = mapHash(this);
}
return _hash;
}
static public int mapHash(IPersistentMap m){
int hash = 0;
for(ISeq s = m.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
hash += (e.getKey() == null ? 0 : Util.hash(e.getKey())) ^
(e.getValue() == null ? 0 : Util.hash(e.getValue()));
}
return hash;
}
public int hasheq(){
if(_hasheq == -1)
{
//this._hasheq = mapHasheq(this);
_hasheq = Murmur3.hashUnordered(this);
}
return _hasheq;
}
static public int mapHasheq(IPersistentMap m) {
return Murmur3.hashUnordered(m);
// int hash = 0;
// for(ISeq s = m.seq(); s != null; s = s.next())
// {
// Map.Entry e = (Map.Entry) s.first();
// hash += Util.hasheq(e.getKey()) ^
// Util.hasheq(e.getValue());
// }
// return hash;
}
static public class KeySeq extends ASeq{
final ISeq seq;
final Iterable iterable;
static public KeySeq create(ISeq seq){
if(seq == null)
return null;
return new KeySeq(seq, null);
}
static public KeySeq createFromMap(IPersistentMap map){
if(map == null)
return null;
ISeq seq = map.seq();
if(seq == null)
return null;
return new KeySeq(seq, map);
}
private KeySeq(ISeq seq, Iterable iterable){
this.seq = seq;
this.iterable = iterable;
}
private KeySeq(IPersistentMap meta, ISeq seq, Iterable iterable){
super(meta);
this.seq = seq;
this.iterable = iterable;
}
public Object first(){
return ((Map.Entry) seq.first()).getKey();
}
public ISeq next(){
return create(seq.next());
}
public KeySeq withMeta(IPersistentMap meta){
return new KeySeq(meta, seq, iterable);
}
public Iterator iterator(){
if(iterable == null)
return super.iterator();
if(iterable instanceof IMapIterable)
return ((IMapIterable)iterable).keyIterator();
final Iterator mapIter = iterable.iterator();
return new Iterator() {
public boolean hasNext() {
return mapIter.hasNext();
}
public Object next() {
return ((Map.Entry)mapIter.next()).getKey();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
static public class ValSeq extends ASeq{
final ISeq seq;
final Iterable iterable;
static public ValSeq create(ISeq seq){
if(seq == null)
return null;
return new ValSeq(seq, null);
}
static public ValSeq createFromMap(IPersistentMap map) {
if(map == null)
return null;
ISeq seq = map.seq();
if(seq == null)
return null;
return new ValSeq(seq, map);
}
private ValSeq(ISeq seq, Iterable iterable){
this.seq = seq;
this.iterable = iterable;
}
private ValSeq(IPersistentMap meta, ISeq seq, Iterable iterable){
super(meta);
this.seq = seq;
this.iterable = iterable;
}
public Object first(){
return ((Map.Entry) seq.first()).getValue();
}
public ISeq next(){
return create(seq.next());
}
public ValSeq withMeta(IPersistentMap meta){
return new ValSeq(meta, seq, iterable);
}
public Iterator iterator(){
if(iterable == null)
return super.iterator();
if(iterable instanceof IMapIterable)
return ((IMapIterable)iterable).valIterator();
final Iterator mapIter = iterable.iterator();
return new Iterator() {
public boolean hasNext() {
return mapIter.hasNext();
}
public Object next() {
return ((Map.Entry)mapIter.next()).getValue();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
static final IFn MAKE_ENTRY = new AFn() {
public Object invoke(Object key, Object val) {
return new MapEntry(key, val);
}
};
static final IFn MAKE_KEY = new AFn() {
public Object invoke(Object key, Object val) {
return key;
}
};
static final IFn MAKE_VAL = new AFn() {
public Object invoke(Object key, Object val) {
return val;
}
};
public Object invoke(Object arg1) {
return valAt(arg1);
}
public Object invoke(Object arg1, Object notFound) {
return valAt(arg1, notFound);
}
// java.util.Map implementation
public void clear(){
throw new UnsupportedOperationException();
}
public boolean containsValue(Object value){
return values().contains(value);
}
public Set entrySet(){
return new AbstractSet(){
public Iterator iterator(){
return APersistentMap.this.iterator();
}
public int size(){
return count();
}
public int hashCode(){
return Util.hash(APersistentMap.this);
}
public boolean contains(Object o){
if(o instanceof Entry)
{
Entry e = (Entry) o;
Entry found = entryAt(e.getKey());
if(found != null && Util.equals(found.getValue(), e.getValue()))
return true;
}
return false;
}
};
}
public Object get(Object key){
return valAt(key);
}
public boolean isEmpty(){
return count() == 0;
}
public Set keySet(){
return new AbstractSet(){
public Iterator iterator(){
final Iterator mi = APersistentMap.this.iterator();
return new Iterator(){
public boolean hasNext(){
return mi.hasNext();
}
public Object next(){
Entry e = (Entry) mi.next();
return e.getKey();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public int size(){
return count();
}
public boolean contains(Object o){
return APersistentMap.this.containsKey(o);
}
};
}
public Object put(Object key, Object value){
throw new UnsupportedOperationException();
}
public void putAll(Map t){
throw new UnsupportedOperationException();
}
public Object remove(Object key){
throw new UnsupportedOperationException();
}
public int size(){
return count();
}
public Collection values(){
return new AbstractCollection(){
public Iterator iterator(){
final Iterator mi = APersistentMap.this.iterator();
return new Iterator(){
public boolean hasNext(){
return mi.hasNext();
}
public Object next(){
Entry e = (Entry) mi.next();
return e.getValue();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public int size(){
return count();
}
};
}
/*
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
if(a.length >= count())
{
ISeq s = seq();
for(int i = 0; s != null; ++i, s = s.rest())
{
a[i] = s.first();
}
if(a.length > count())
a[count()] = null;
return a;
}
else
return toArray();
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public boolean contains(Object o){
if(o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
Map.Entry v = entryAt(e.getKey());
return (v != null && Util.equal(v.getValue(), e.getValue()));
}
return false;
}
*/
}
================================================
FILE: src/jvm/clojure/lang/APersistentSet.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable, IHashEq {
int _hash = -1;
int _hasheq = -1;
final IPersistentMap impl;
protected APersistentSet(IPersistentMap impl){
this.impl = impl;
}
public String toString(){
return RT.printString(this);
}
public boolean contains(Object key){
return impl.containsKey(key);
}
public Object get(Object key){
return impl.valAt(key);
}
public int count(){
return impl.count();
}
public ISeq seq(){
return RT.keys(impl);
}
public Object invoke(Object arg1) {
return get(arg1);
}
public boolean equals(Object obj){
return setEquals(this, obj);
}
static public boolean setEquals(IPersistentSet s1, Object obj) {
if(s1 == obj) return true;
if(!(obj instanceof Set))
return false;
Set m = (Set) obj;
if(m.size() != s1.count())
return false;
for(Object aM : m)
{
if(!s1.contains(aM))
return false;
}
return true;
}
public boolean equiv(Object obj){
if (!(obj instanceof Set))
return false;
Set m = (Set) obj;
if (m.size() != size())
return false;
for(Object aM : m)
{
if(!contains(aM))
return false;
}
return true;
}
public int hashCode(){
if(_hash == -1)
{
//int hash = count();
int hash = 0;
for(ISeq s = seq(); s != null; s = s.next())
{
Object e = s.first();
// hash = Util.hashCombine(hash, Util.hash(e));
hash += Util.hash(e);
}
this._hash = hash;
}
return _hash;
}
public int hasheq(){
if(_hasheq == -1){
// int hash = 0;
// for(ISeq s = seq(); s != null; s = s.next())
// {
// Object e = s.first();
// hash += Util.hasheq(e);
// }
// this._hasheq = hash;
_hasheq = Murmur3.hashUnordered(this);
}
return _hasheq;
}
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
return RT.seqToPassedArray(seq(), a);
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public Iterator iterator(){
if(impl instanceof IMapIterable)
return ((IMapIterable)impl).keyIterator();
else return new Iterator() {
private final Iterator iter = impl.iterator();
public boolean hasNext() {
return iter.hasNext();
}
public Object next() {
return ((IMapEntry)iter.next()).key();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
================================================
FILE: src/jvm/clojure/lang/APersistentVector.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 18, 2007 */
package clojure.lang;
import java.io.Serializable;
import java.util.*;
public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
List,
RandomAccess, Comparable,
Serializable, IHashEq {
int _hash = -1;
int _hasheq = -1;
public String toString(){
return RT.printString(this);
}
public ISeq seq(){
if(count() > 0)
return new Seq(this, 0);
return null;
}
public ISeq rseq(){
if(count() > 0)
return new RSeq(this, count() - 1);
return null;
}
static boolean doEquals(IPersistentVector v, Object obj){
if(v == obj) return true;
if(obj instanceof List || obj instanceof IPersistentVector)
{
Collection ma = (Collection) obj;
if(ma.size() != v.count() || Util.hash(ma) != Util.hash(v))
return false;
for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator();
i1.hasNext();)
{
if(!Util.equals(i1.next(), i2.next()))
return false;
}
return true;
}
// if(obj instanceof IPersistentVector)
// {
// IPersistentVector ma = (IPersistentVector) obj;
// if(ma.count() != v.count() || Util.hash(ma) != Util.hash(v))
// return false;
// for(int i = 0; i < v.count(); i++)
// {
// if(!Util.equal(v.nth(i), ma.nth(i)))
// return false;
// }
// }
else
{
if(!(obj instanceof Sequential))
return false;
ISeq ms = RT.seq(obj);
for(int i = 0; i < v.count(); i++, ms = ms.next())
{
if(ms == null || !Util.equals(v.nth(i), ms.first()))
return false;
}
if(ms != null)
return false;
}
return true;
}
static boolean doEquiv(IPersistentVector v, Object obj){
if(obj instanceof List || obj instanceof IPersistentVector)
{
Collection ma = (Collection) obj;
if(ma.size() != v.count())
return false;
for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator();
i1.hasNext();)
{
if(!Util.equiv(i1.next(), i2.next()))
return false;
}
return true;
}
// if(obj instanceof IPersistentVector)
// {
// IPersistentVector ma = (IPersistentVector) obj;
// if(ma.count() != v.count() || Util.hash(ma) != Util.hash(v))
// return false;
// for(int i = 0; i < v.count(); i++)
// {
// if(!Util.equal(v.nth(i), ma.nth(i)))
// return false;
// }
// }
else
{
if(!(obj instanceof Sequential))
return false;
ISeq ms = RT.seq(obj);
for(int i = 0; i < v.count(); i++, ms = ms.next())
{
if(ms == null || !Util.equiv(v.nth(i), ms.first()))
return false;
}
if(ms != null)
return false;
}
return true;
}
public boolean equals(Object obj){
return doEquals(this, obj);
}
public boolean equiv(Object obj){
return doEquiv(this, obj);
}
public int hashCode(){
if(_hash == -1)
{
int hash = 1;
Iterator i = iterator();
while(i.hasNext())
{
Object obj = i.next();
hash = 31 * hash + (obj == null ? 0 : Util.hash(obj));
}
// int hash = 0;
// for(int i = 0; i < count(); i++)
// {
// hash = Util.hashCombine(hash, Util.hash(nth(i)));
// }
this._hash = hash;
}
return _hash;
}
public int hasheq(){
if(_hasheq == -1) {
// int hash = 1;
// Iterator i = iterator();
// while(i.hasNext())
// {
// Object obj = i.next();
// hash = 31 * hash + Util.hasheq(obj);
// }
// _hasheq = hash;
_hasheq = Murmur3.hashOrdered(this);
}
return _hasheq;
}
public Object get(int index){
return nth(index);
}
public Object nth(int i, Object notFound){
if(i >= 0 && i < count())
return nth(i);
return notFound;
}
public Object remove(int i){
throw new UnsupportedOperationException();
}
public int indexOf(Object o){
for(int i = 0; i < count(); i++)
if(Util.equiv(nth(i), o))
return i;
return -1;
}
public int lastIndexOf(Object o){
for(int i = count() - 1; i >= 0; i--)
if(Util.equiv(nth(i), o))
return i;
return -1;
}
public ListIterator listIterator(){
return listIterator(0);
}
public ListIterator listIterator(final int index){
return new ListIterator(){
int nexti = index;
public boolean hasNext(){
return nexti < count();
}
public Object next(){
return nth(nexti++);
}
public boolean hasPrevious(){
return nexti > 0;
}
public Object previous(){
return nth(--nexti);
}
public int nextIndex(){
return nexti;
}
public int previousIndex(){
return nexti - 1;
}
public void remove(){
throw new UnsupportedOperationException();
}
public void set(Object o){
throw new UnsupportedOperationException();
}
public void add(Object o){
throw new UnsupportedOperationException();
}
};
}
Iterator rangedIterator(final int start, final int end){
return new Iterator(){
int i = start;
public boolean hasNext(){
return i < end;
}
public Object next(){
return nth(i++);
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public List subList(int fromIndex, int toIndex){
return (List) RT.subvec(this, fromIndex, toIndex);
}
public Object set(int i, Object o){
throw new UnsupportedOperationException();
}
public void add(int i, Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(int i, Collection c){
throw new UnsupportedOperationException();
}
public Object invoke(Object arg1) {
if(Util.isInteger(arg1))
return nth(((Number) arg1).intValue());
throw new IllegalArgumentException("Key must be integer");
}
public Iterator iterator(){
//todo - something more efficient
return new Iterator(){
int i = 0;
public boolean hasNext(){
return i < count();
}
public Object next(){
return nth(i++);
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public Object peek(){
if(count() > 0)
return nth(count() - 1);
return null;
}
public boolean containsKey(Object key){
if(!(Util.isInteger(key)))
return false;
int i = ((Number) key).intValue();
return i >= 0 && i < count();
}
public IMapEntry entryAt(Object key){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < count())
return new MapEntry(key, nth(i));
}
return null;
}
public IPersistentVector assoc(Object key, Object val){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
return assocN(i, val);
}
throw new IllegalArgumentException("Key must be integer");
}
public Object valAt(Object key, Object notFound){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < count())
return nth(i);
}
return notFound;
}
public Object valAt(Object key){
return valAt(key, null);
}
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
return RT.seqToPassedArray(seq(), a);
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.next())
{
if(Util.equiv(s.first(), o))
return true;
}
return false;
}
public int length(){
return count();
}
public int compareTo(Object o){
IPersistentVector v = (IPersistentVector) o;
if(count() < v.count())
return -1;
else if(count() > v.count())
return 1;
for(int i = 0; i < count(); i++)
{
int c = Util.compare(nth(i),v.nth(i));
if(c != 0)
return c;
}
return 0;
}
static class Seq extends ASeq implements IndexedSeq, IReduce{
//todo - something more efficient
final IPersistentVector v;
final int i;
public Seq(IPersistentVector v, int i){
this.v = v;
this.i = i;
}
Seq(IPersistentMap meta, IPersistentVector v, int i){
super(meta);
this.v = v;
this.i = i;
}
public Object first(){
return v.nth(i);
}
public ISeq next(){
if(i + 1 < v.count())
return new APersistentVector.Seq(v, i + 1);
return null;
}
public int index(){
return i;
}
public int count(){
return v.count() - i;
}
public APersistentVector.Seq withMeta(IPersistentMap meta){
return new APersistentVector.Seq(meta, v, i);
}
public Object reduce(IFn f) {
Object ret = v.nth(i);
for(int x = i + 1; x < v.count(); x++) {
ret = f.invoke(ret, v.nth(x));
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, v.nth(i));
for(int x = i + 1; x < v.count(); x++) {
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
ret = f.invoke(ret, v.nth(x));
}
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
return ret;
}
}
public static class RSeq extends ASeq implements IndexedSeq, Counted{
final IPersistentVector v;
final int i;
public RSeq(IPersistentVector vector, int i){
this.v = vector;
this.i = i;
}
RSeq(IPersistentMap meta, IPersistentVector v, int i){
super(meta);
this.v = v;
this.i = i;
}
public Object first(){
return v.nth(i);
}
public ISeq next(){
if(i > 0)
return new APersistentVector.RSeq(v, i - 1);
return null;
}
public int index(){
return i;
}
public int count(){
return i + 1;
}
public APersistentVector.RSeq withMeta(IPersistentMap meta){
return new APersistentVector.RSeq(meta, v, i);
}
}
public static class SubVector extends APersistentVector implements IObj{
public final IPersistentVector v;
public final int start;
public final int end;
final IPersistentMap _meta;
public SubVector(IPersistentMap meta, IPersistentVector v, int start, int end){
this._meta = meta;
if(v instanceof APersistentVector.SubVector)
{
APersistentVector.SubVector sv = (APersistentVector.SubVector) v;
start += sv.start;
end += sv.start;
v = sv.v;
}
this.v = v;
this.start = start;
this.end = end;
}
public Iterator iterator(){
if (v instanceof APersistentVector) {
return ((APersistentVector)v).rangedIterator(start,end);
}
return super.iterator();
}
public Object nth(int i){
if((start + i >= end) || (i < 0))
throw new IndexOutOfBoundsException();
return v.nth(start + i);
}
public IPersistentVector assocN(int i, Object val){
if(start + i > end)
throw new IndexOutOfBoundsException();
else if(start + i == end)
return cons(val);
return new SubVector(_meta, v.assocN(start + i, val), start, end);
}
public int count(){
return end - start;
}
public IPersistentVector cons(Object o){
return new SubVector(_meta, v.assocN(end, o), start, end + 1);
}
public IPersistentCollection empty(){
return PersistentVector.EMPTY.withMeta(meta());
}
public IPersistentStack pop(){
if(end - 1 == start)
{
return PersistentVector.EMPTY;
}
return new SubVector(_meta, v, start, end - 1);
}
public SubVector withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
return new SubVector(meta, v, start, end);
}
public IPersistentMap meta(){
return _meta;
}
}
}
================================================
FILE: src/jvm/clojure/lang/ARef.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 1, 2009 */
package clojure.lang;
import java.util.Map;
public abstract class ARef extends AReference implements IRef{
protected volatile IFn validator = null;
private volatile IPersistentMap watches = PersistentHashMap.EMPTY;
public ARef(){
super();
}
public ARef(IPersistentMap meta){
super(meta);
}
void validate(IFn vf, Object val){
try
{
if(vf != null && !RT.booleanCast(vf.invoke(val)))
throw new IllegalStateException("Invalid reference state");
}
catch(RuntimeException re)
{
throw re;
}
catch(Exception e)
{
throw new IllegalStateException("Invalid reference state", e);
}
}
void validate(Object val){
validate(validator, val);
}
public void setValidator(IFn vf){
validate(vf, deref());
validator = vf;
}
public IFn getValidator(){
return validator;
}
public IPersistentMap getWatches(){
return watches;
}
synchronized public IRef addWatch(Object key, IFn callback){
watches = watches.assoc(key, callback);
return this;
}
synchronized public IRef removeWatch(Object key){
watches = watches.without(key);
return this;
}
public void notifyWatches(Object oldval, Object newval){
IPersistentMap ws = watches;
if(ws.count() > 0)
{
for(ISeq s = ws.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
IFn fn = (IFn) e.getValue();
if(fn != null)
fn.invoke(e.getKey(), this, oldval, newval);
}
}
}
}
================================================
FILE: src/jvm/clojure/lang/AReference.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 31, 2008 */
package clojure.lang;
public class AReference implements IReference {
private IPersistentMap _meta;
public AReference() {
this(null);
}
public AReference(IPersistentMap meta) {
_meta = meta;
}
synchronized public IPersistentMap meta() {
return _meta;
}
synchronized public IPersistentMap alterMeta(IFn alter, ISeq args) {
_meta = (IPersistentMap) alter.applyTo(new Cons(_meta, args));
return _meta;
}
synchronized public IPersistentMap resetMeta(IPersistentMap m) {
_meta = m;
return m;
}
}
================================================
FILE: src/jvm/clojure/lang/ASeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.*;
public abstract class ASeq extends Obj implements ISeq, Sequential, List, Serializable, IHashEq {
transient int _hash = -1;
transient int _hasheq = -1;
public String toString(){
return RT.printString(this);
}
public IPersistentCollection empty(){
return PersistentList.EMPTY;
}
protected ASeq(IPersistentMap meta){
super(meta);
}
protected ASeq(){
}
public boolean equiv(Object obj){
if(!(obj instanceof Sequential || obj instanceof List))
return false;
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
if(ms == null || !Util.equiv(s.first(), ms.first()))
return false;
}
return ms == null;
}
public boolean equals(Object obj){
if(this == obj) return true;
if(!(obj instanceof Sequential || obj instanceof List))
return false;
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
if(ms == null || !Util.equals(s.first(), ms.first()))
return false;
}
return ms == null;
}
public int hashCode(){
if(_hash == -1)
{
int hash = 1;
for(ISeq s = seq(); s != null; s = s.next())
{
hash = 31 * hash + (s.first() == null ? 0 : Util.hash(s.first()));
}
this._hash = hash;
}
return _hash;
}
public int hasheq(){
if(_hasheq == -1)
{
// int hash = 1;
// for(ISeq s = seq(); s != null; s = s.next())
// {
// hash = 31 * hash + Util.hasheq(s.first());
// }
// this._hasheq = hash;
_hasheq = Murmur3.hashOrdered(this);
}
return _hasheq;
}
//public Object reduce(IFn f) {
// Object ret = first();
// for(ISeq s = rest(); s != null; s = s.rest())
// ret = f.invoke(ret, s.first());
// return ret;
//}
//
//public Object reduce(IFn f, Object start) {
// Object ret = f.invoke(start, first());
// for(ISeq s = rest(); s != null; s = s.rest())
// ret = f.invoke(ret, s.first());
// return ret;
//}
//public Object peek(){
// return first();
//}
//
//public IPersistentList pop(){
// return rest();
//}
public int count(){
int i = 1;
for(ISeq s = next(); s != null; s = s.next(), i++)
if(s instanceof Counted)
return i + s.count();
return i;
}
final public ISeq seq(){
return this;
}
public ISeq cons(Object o){
return new Cons(o, this);
}
public ISeq more(){
ISeq s = next();
if(s == null)
return PersistentList.EMPTY;
return s;
}
//final public ISeq rest(){
// Seqable m = more();
// if(m == null)
// return null;
// return m.seq();
//}
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
return RT.seqToPassedArray(seq(), a);
}
public int size(){
return count();
}
public boolean isEmpty(){
return seq() == null;
}
public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.next())
{
if(Util.equiv(s.first(), o))
return true;
}
return false;
}
public Iterator iterator(){
return new SeqIterator(this);
}
//////////// List stuff /////////////////
private List reify(){
return Collections.unmodifiableList(new ArrayList(this));
}
public List subList(int fromIndex, int toIndex){
return reify().subList(fromIndex, toIndex);
}
public Object set(int index, Object element){
throw new UnsupportedOperationException();
}
public Object remove(int index){
throw new UnsupportedOperationException();
}
public int indexOf(Object o){
ISeq s = seq();
for(int i = 0; s != null; s = s.next(), i++)
{
if(Util.equiv(s.first(), o))
return i;
}
return -1;
}
public int lastIndexOf(Object o){
return reify().lastIndexOf(o);
}
public ListIterator listIterator(){
return reify().listIterator();
}
public ListIterator listIterator(int index){
return reify().listIterator(index);
}
public Object get(int index){
return RT.nth(this, index);
}
public void add(int index, Object element){
throw new UnsupportedOperationException();
}
public boolean addAll(int index, Collection c){
throw new UnsupportedOperationException();
}
}
================================================
FILE: src/jvm/clojure/lang/ATransientMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.util.Map;
import clojure.lang.PersistentHashMap.INode;
abstract class ATransientMap extends AFn implements ITransientMap {
abstract void ensureEditable();
abstract ITransientMap doAssoc(Object key, Object val);
abstract ITransientMap doWithout(Object key);
abstract Object doValAt(Object key, Object notFound);
abstract int doCount();
abstract IPersistentMap doPersistent();
public ITransientMap conj(Object o) {
ensureEditable();
if(o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
return assoc(e.getKey(), e.getValue());
}
else if(o instanceof IPersistentVector)
{
IPersistentVector v = (IPersistentVector) o;
if(v.count() != 2)
throw new IllegalArgumentException("Vector arg to map conj must be a pair");
return assoc(v.nth(0), v.nth(1));
}
ITransientMap ret = this;
for(ISeq es = RT.seq(o); es != null; es = es.next())
{
Map.Entry e = (Map.Entry) es.first();
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret;
}
public final Object invoke(Object arg1) {
return valAt(arg1);
}
public final Object invoke(Object arg1, Object notFound) {
return valAt(arg1, notFound);
}
public final Object valAt(Object key) {
return valAt(key, null);
}
public final ITransientMap assoc(Object key, Object val) {
ensureEditable();
return doAssoc(key, val);
}
public final ITransientMap without(Object key) {
ensureEditable();
return doWithout(key);
}
public final IPersistentMap persistent() {
ensureEditable();
return doPersistent();
}
public final Object valAt(Object key, Object notFound) {
ensureEditable();
return doValAt(key, notFound);
}
public final int count() {
ensureEditable();
return doCount();
}
}
================================================
FILE: src/jvm/clojure/lang/ATransientSet.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
public abstract class ATransientSet extends AFn implements ITransientSet{
volatile ITransientMap impl;
ATransientSet(ITransientMap impl) {
this.impl = impl;
}
public int count() {
return impl.count();
}
public ITransientSet conj(Object val) {
ITransientMap m = impl.assoc(val, val);
if (m != impl) this.impl = m;
return this;
}
public boolean contains(Object key) {
return this != impl.valAt(key, this);
}
public ITransientSet disjoin(Object key) {
ITransientMap m = impl.without(key);
if (m != impl) this.impl = m;
return this;
}
public Object get(Object key) {
return impl.valAt(key);
}
public Object invoke(Object key, Object notFound) {
return impl.valAt(key, notFound);
}
public Object invoke(Object key) {
return impl.valAt(key);
}
}
================================================
FILE: src/jvm/clojure/lang/Agent.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 17, 2007 */
package clojure.lang;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
public class Agent extends ARef {
static class ActionQueue {
public final IPersistentStack q;
public final Throwable error; // non-null indicates fail state
static final ActionQueue EMPTY = new ActionQueue(PersistentQueue.EMPTY, null);
public ActionQueue( IPersistentStack q, Throwable error )
{
this.q = q;
this.error = error;
}
}
static final Keyword CONTINUE = Keyword.intern(null, "continue");
static final Keyword FAIL = Keyword.intern(null, "fail");
volatile Object state;
AtomicReference aq = new AtomicReference(ActionQueue.EMPTY);
volatile Keyword errorMode = CONTINUE;
volatile IFn errorHandler = null;
//final private static AtomicLong sendThreadPoolCounter = new AtomicLong(0);
//final private static AtomicLong sendOffThreadPoolCounter = new AtomicLong(0);
volatile public static java.util.concurrent.ExecutorService pooledExecutor =
Executors.newFixedThreadPool(2 + Runtime.getRuntime().availableProcessors());
//,createThreadFactory("clojure-agent-send-pool-%d", sendThreadPoolCounter)
volatile public static java.util.concurrent.ExecutorService soloExecutor = Executors.newCachedThreadPool();
//createThreadFactory("clojure-agent-send-off-pool-%d", sendOffThreadPoolCounter)
final static ThreadLocal nested = new ThreadLocal();
//private static java.util.concurrent.ThreadFactory createThreadFactory(final String format, final AtomicLong threadPoolCounter) {
// return new java.util.concurrent.ThreadFactory() {
// public Thread newThread(Runnable runnable) {
// Thread thread = new Thread(runnable);
// thread.setName(String.format(format, threadPoolCounter.getAndIncrement()));
// return thread;
// }
// };
//}
public static void shutdown(){
soloExecutor.shutdown();
pooledExecutor.shutdown();
}
static class Action implements Runnable{
final Agent agent;
final IFn fn;
final ISeq args;
final Executor exec;
public Action(Agent agent, IFn fn, ISeq args, Executor exec){
this.agent = agent;
this.args = args;
this.fn = fn;
this.exec = exec;
}
void execute(){
try
{
exec.execute(this);
}
catch(Throwable error)
{
if(agent.errorHandler != null)
{
try
{
agent.errorHandler.invoke(agent, error);
}
catch(Throwable e) {} // ignore errorHandler errors
}
}
}
static void doRun(Action action){
try
{
nested.set(PersistentVector.EMPTY);
Throwable error = null;
try
{
Object oldval = action.agent.state;
Object newval = action.fn.applyTo(RT.cons(action.agent.state, action.args));
action.agent.setState(newval);
action.agent.notifyWatches(oldval,newval);
}
catch(Throwable e)
{
error = e;
}
if(error == null)
{
releasePendingSends();
}
else
{
nested.set(null); // allow errorHandler to send
if(action.agent.errorHandler != null)
{
try
{
action.agent.errorHandler.invoke(action.agent, error);
}
catch(Throwable e) {} // ignore errorHandler errors
}
if(action.agent.errorMode == CONTINUE)
{
error = null;
}
}
boolean popped = false;
ActionQueue next = null;
while(!popped)
{
ActionQueue prior = action.agent.aq.get();
next = new ActionQueue(prior.q.pop(), error);
popped = action.agent.aq.compareAndSet(prior, next);
}
if(error == null && next.q.count() > 0)
((Action) next.q.peek()).execute();
}
finally
{
nested.set(null);
}
}
public void run(){
doRun(this);
}
}
public Agent(Object state) {
this(state,null);
}
public Agent(Object state, IPersistentMap meta) {
super(meta);
setState(state);
}
boolean setState(Object newState) {
validate(newState);
boolean ret = state != newState;
state = newState;
return ret;
}
public Object deref() {
return state;
}
public Throwable getError(){
return aq.get().error;
}
public void setErrorMode(Keyword k){
errorMode = k;
}
public Keyword getErrorMode(){
return errorMode;
}
public void setErrorHandler(IFn f){
errorHandler = f;
}
public IFn getErrorHandler(){
return errorHandler;
}
synchronized public Object restart(Object newState, boolean clearActions){
if(getError() == null)
{
throw Util.runtimeException("Agent does not need a restart");
}
validate(newState);
state = newState;
if(clearActions)
aq.set(ActionQueue.EMPTY);
else
{
boolean restarted = false;
ActionQueue prior = null;
while(!restarted)
{
prior = aq.get();
restarted = aq.compareAndSet(prior, new ActionQueue(prior.q, null));
}
if(prior.q.count() > 0)
((Action) prior.q.peek()).execute();
}
return newState;
}
public Object dispatch(IFn fn, ISeq args, Executor exec) {
Throwable error = getError();
if(error != null)
{
throw Util.runtimeException("Agent is failed, needs restart", error);
}
Action action = new Action(this, fn, args, exec);
dispatchAction(action);
return this;
}
static void dispatchAction(Action action){
LockingTransaction trans = LockingTransaction.getRunning();
if(trans != null)
trans.enqueue(action);
else if(nested.get() != null)
{
nested.set(nested.get().cons(action));
}
else
action.agent.enqueue(action);
}
void enqueue(Action action){
boolean queued = false;
ActionQueue prior = null;
while(!queued)
{
prior = aq.get();
queued = aq.compareAndSet(prior, new ActionQueue((IPersistentStack)prior.q.cons(action), prior.error));
}
if(prior.q.count() == 0 && prior.error == null)
action.execute();
}
public int getQueueCount(){
return aq.get().q.count();
}
static public int releasePendingSends(){
IPersistentVector sends = nested.get();
if(sends == null)
return 0;
for(int i=0;i= 0 && i < count())
return nth(i);
return notFound;
}
public int count(){
return end - off;
}
public IChunk dropFirst(){
if(off==end)
throw new IllegalStateException("dropFirst of empty chunk");
return new ArrayChunk(array, off + 1, end);
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[off]);
if(RT.isReduced(ret))
return ret;
for(int x = off + 1; x < end; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ret;
}
return ret;
}
}
================================================
FILE: src/jvm/clojure/lang/ArrayIter.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.lang.reflect.Array;
import java.util.Iterator;
public class ArrayIter implements Iterator {
final Object[] array;
int i;
static public Iterator EMPTY_ITERATOR = new Iterator() {
public boolean hasNext() { return false; }
public Object next() { return null; }
public void remove() { throw new UnsupportedOperationException("remove() not supported"); }
};
static public Iterator create(){
return EMPTY_ITERATOR;
}
static public Iterator create(Object... array){
if(array == null || array.length == 0)
return EMPTY_ITERATOR;
return new ArrayIter(array, 0);
}
static public Iterator createFromObject(Object array){
if(array == null || Array.getLength(array) == 0)
return EMPTY_ITERATOR;
Class aclass = array.getClass();
if(aclass == int[].class)
return new ArrayIter_int((int[]) array, 0);
if(aclass == float[].class)
return new ArrayIter_float((float[]) array, 0);
if(aclass == double[].class)
return new ArrayIter_double((double[]) array, 0);
if(aclass == long[].class)
return new ArrayIter_long((long[]) array, 0);
if(aclass == byte[].class)
return new ArrayIter_byte((byte[]) array, 0);
if(aclass == char[].class)
return new ArrayIter_char((char[]) array, 0);
if(aclass == short[].class)
return new ArrayIter_short((short[]) array, 0);
if(aclass == boolean[].class)
return new ArrayIter_boolean((boolean[]) array, 0);
return new ArrayIter(array, 0);
}
ArrayIter(Object array, int i){
this.i = i;
this.array = (Object[]) array;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Object next() {
if(array != null && i < array.length)
return array[i++];
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
//////////////////////////////////// specialized primitive versions ///////////////////////////////
static public class ArrayIter_int implements Iterator {
final int[] array;
int i;
ArrayIter_int(int[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Long next() {
if(array != null && i < array.length)
return Long.valueOf(array[i++]);
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_float implements Iterator {
final float[] array;
int i;
ArrayIter_float(float[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Double next() {
if(array != null && i < array.length)
return Double.valueOf(array[i++]);
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_double implements Iterator {
final double[] array;
int i;
ArrayIter_double(double[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Double next() {
if(array != null && i < array.length)
return array[i++];
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_long implements Iterator {
final long[] array;
int i;
ArrayIter_long(long[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Long next() {
if(array != null && i < array.length)
return Long.valueOf(array[i++]);
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_byte implements Iterator {
final byte[] array;
int i;
ArrayIter_byte(byte[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Byte next() {
if(array != null && i < array.length)
return array[i++];
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_char implements Iterator {
final char[] array;
int i;
ArrayIter_char(char[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Character next() {
if(array != null && i < array.length)
return array[i++];
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_short implements Iterator {
final short[] array;
int i;
ArrayIter_short(short[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Long next() {
if(array != null && i < array.length)
return Long.valueOf(array[i++]);
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
static public class ArrayIter_boolean implements Iterator {
final boolean[] array;
int i;
ArrayIter_boolean(boolean[] array, int i){
this.array = array;
this.i = i;
}
public boolean hasNext() {
return array != null && i < array.length;
}
public Boolean next() {
if(array != null && i < array.length)
return Boolean.valueOf(array[i++]);
return null;
}
public void remove() {
throw new UnsupportedOperationException("remove() not supported");
}
}
}
================================================
FILE: src/jvm/clojure/lang/ArraySeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jun 19, 2006 */
package clojure.lang;
import java.lang.reflect.Array;
public class ArraySeq extends ASeq implements IndexedSeq, IReduce{
public final Object[] array;
final int i;
//ISeq _rest;
static public ArraySeq create(){
return null;
}
static public ArraySeq create(Object... array){
if(array == null || array.length == 0)
return null;
return new ArraySeq(array, 0);
}
static ISeq createFromObject(Object array){
if(array == null || Array.getLength(array) == 0)
return null;
Class aclass = array.getClass();
if(aclass == int[].class)
return new ArraySeq_int(null, (int[]) array, 0);
if(aclass == float[].class)
return new ArraySeq_float(null, (float[]) array, 0);
if(aclass == double[].class)
return new ArraySeq_double(null, (double[]) array, 0);
if(aclass == long[].class)
return new ArraySeq_long(null, (long[]) array, 0);
if(aclass == byte[].class)
return new ArraySeq_byte(null, (byte[]) array, 0);
if(aclass == char[].class)
return new ArraySeq_char(null, (char[]) array, 0);
if(aclass == short[].class)
return new ArraySeq_short(null, (short[]) array, 0);
if(aclass == boolean[].class)
return new ArraySeq_boolean(null, (boolean[]) array, 0);
return new ArraySeq(array, 0);
}
ArraySeq(Object array, int i){
this.i = i;
this.array = (Object[]) array;
// this._rest = this;
}
ArraySeq(IPersistentMap meta, Object array, int i){
super(meta);
this.i = i;
this.array = (Object[]) array;
}
public Object first(){
if(array != null)
return array[i];
return null;
}
public ISeq next(){
if(array != null && i + 1 < array.length)
return new ArraySeq(array, i + 1);
return null;
}
public int count(){
if(array != null)
return array.length - i;
return 0;
}
public int index(){
return i;
}
public ArraySeq withMeta(IPersistentMap meta){
return new ArraySeq(meta, array, i);
}
public Object reduce(IFn f) {
if(array != null) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
return null;
}
public Object reduce(IFn f, Object start) {
if(array != null) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
return null;
}
public int indexOf(Object o) {
if(array != null)
for (int j = i; j < array.length; j++)
if (Util.equals(o, array[j])) return j - i;
return -1;
}
public int lastIndexOf(Object o) {
if (array != null) {
if (o == null) {
for (int j = array.length - 1 ; j >= i; j--)
if (array[j] == null) return j - i;
} else {
for (int j = array.length - 1 ; j >= i; j--)
if (o.equals(array[j])) return j - i;
}
}
return -1;
}
//////////////////////////////////// specialized primitive versions ///////////////////////////////
static public class ArraySeq_int extends ASeq implements IndexedSeq, IReduce{
public final int[] array;
final int i;
ArraySeq_int(IPersistentMap meta, int[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return array[i];
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_int(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_int withMeta(IPersistentMap meta){
return new ArraySeq_int(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Number) {
int k = ((Number) o).intValue();
for (int j = i; j < array.length; j++)
if (k == array[j]) return j - i;
}
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Number) {
int k = ((Number) o).intValue();
for (int j = array.length - 1; j >= i; j--)
if (k == array[j]) return j - i;
}
return -1;
}
}
static public class ArraySeq_float extends ASeq implements IndexedSeq, IReduce{
public final float[] array;
final int i;
ArraySeq_float(IPersistentMap meta, float[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return Numbers.num(array[i]);
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_float(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_float withMeta(IPersistentMap meta){
return new ArraySeq_float(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = Numbers.num(array[i]);
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, Numbers.num(array[x]));
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, Numbers.num(array[i]));
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, Numbers.num(array[x]));
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Number) {
float f = ((Number) o).floatValue();
for (int j = i; j < array.length; j++)
if (f == array[j]) return j - i;
}
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Number) {
float f = ((Number) o).floatValue();
for (int j = array.length - 1; j >= i; j--)
if (f == array[j]) return j - i;
}
return -1;
}
}
static public class ArraySeq_double extends ASeq implements IndexedSeq, IReduce{
public final double[] array;
final int i;
ArraySeq_double(IPersistentMap meta, double[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return array[i];
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_double(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_double withMeta(IPersistentMap meta){
return new ArraySeq_double(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Number) {
double d = ((Number) o).doubleValue();
for (int j = i; j < array.length; j++)
if (d == array[j]) return j - i;
}
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Number) {
double d = ((Number) o).doubleValue();
for (int j = array.length - 1; j >= i; j--)
if (d == array[j]) return j - i;
}
return -1;
}
}
static public class ArraySeq_long extends ASeq implements IndexedSeq, IReduce{
public final long[] array;
final int i;
ArraySeq_long(IPersistentMap meta, long[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return Numbers.num(array[i]);
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_long(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_long withMeta(IPersistentMap meta){
return new ArraySeq_long(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = Numbers.num(array[i]);
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, Numbers.num(array[x]));
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, Numbers.num(array[i]));
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, Numbers.num(array[x]));
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Number) {
long l = ((Number) o).longValue();
for (int j = i; j < array.length; j++)
if (l == array[j]) return j - i;
}
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Number) {
long l = ((Number) o).longValue();
for (int j = array.length - 1; j >= i; j--)
if (l == array[j]) return j - i;
}
return -1;
}
}
static public class ArraySeq_byte extends ASeq implements IndexedSeq, IReduce{
public final byte[] array;
final int i;
ArraySeq_byte(IPersistentMap meta, byte[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return array[i];
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_byte(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_byte withMeta(IPersistentMap meta){
return new ArraySeq_byte(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Byte) {
byte b = ((Byte) o).byteValue();
for (int j = i; j < array.length; j++)
if (b == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = i; j < array.length; j++)
if (o.equals(array[j])) return j - i;
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Byte) {
byte b = ((Byte) o).byteValue();
for (int j = array.length - 1; j >= i; j--)
if (b == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = array.length - 1; j >= i; j--)
if (o.equals(array[j])) return j - i;
return -1;
}
}
static public class ArraySeq_char extends ASeq implements IndexedSeq, IReduce{
public final char[] array;
final int i;
ArraySeq_char(IPersistentMap meta, char[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return array[i];
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_char(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_char withMeta(IPersistentMap meta){
return new ArraySeq_char(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Character) {
char c = ((Character) o).charValue();
for (int j = i; j < array.length; j++)
if (c == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = i; j < array.length; j++)
if (o.equals(array[j])) return j - i;
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Character) {
char c = ((Character) o).charValue();
for (int j = array.length - 1; j >= i; j--)
if (c == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = array.length - 1; j >= i; j--)
if (o.equals(array[j])) return j - i;
return -1;
}
}
static public class ArraySeq_short extends ASeq implements IndexedSeq, IReduce{
public final short[] array;
final int i;
ArraySeq_short(IPersistentMap meta, short[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return array[i];
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_short(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_short withMeta(IPersistentMap meta){
return new ArraySeq_short(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Short) {
short s = ((Short) o).shortValue();
for (int j = i; j < array.length; j++)
if (s == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = i; j < array.length; j++)
if (o.equals(array[j])) return j - i;
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Short) {
short s = ((Short) o).shortValue();
for (int j = array.length - 1; j >= i; j--)
if (s == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = array.length - 1; j >= i; j--)
if (o.equals(array[j])) return j - i;
return -1;
}
}
static public class ArraySeq_boolean extends ASeq implements IndexedSeq, IReduce{
public final boolean[] array;
final int i;
ArraySeq_boolean(IPersistentMap meta, boolean[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return array[i];
}
public ISeq next(){
if(i + 1 < array.length)
return new ArraySeq_boolean(meta(), array, i + 1);
return null;
}
public int count(){
return array.length - i;
}
public int index(){
return i;
}
public ArraySeq_boolean withMeta(IPersistentMap meta){
return new ArraySeq_boolean(meta, array, i);
}
public Object reduce(IFn f) {
Object ret = array[i];
for(int x = i + 1; x < array.length; x++)
{
ret = f.invoke(ret, array[x]);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, array[i]);
for(int x = i + 1; x < array.length; x++)
{
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
ret = f.invoke(ret, array[x]);
}
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
return ret;
}
public int indexOf(Object o) {
if (o instanceof Boolean) {
boolean b = ((Boolean) o).booleanValue();
for (int j = i; j < array.length; j++)
if (b == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = i; j < array.length; j++)
if (o.equals(array[j])) return j - i;
return -1;
}
public int lastIndexOf(Object o) {
if (o instanceof Boolean) {
boolean b = ((Boolean) o).booleanValue();
for (int j = array.length - 1; j >= i; j--)
if (b == array[j]) return j - i;
}
if (o == null) {
return -1;
}
for (int j = array.length - 1; j >= i; j--)
if (o.equals(array[j])) return j - i;
return -1;
}
}
}
================================================
FILE: src/jvm/clojure/lang/Associative.java
================================================
package clojure.lang;
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
public interface Associative extends IPersistentCollection, ILookup{
boolean containsKey(Object key);
IMapEntry entryAt(Object key);
Associative assoc(Object key, Object val);
}
================================================
FILE: src/jvm/clojure/lang/Atom.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 1, 2009 */
package clojure.lang;
import java.util.concurrent.atomic.AtomicReference;
final public class Atom extends ARef implements IAtom{
final AtomicReference state;
public Atom(Object state){
this.state = new AtomicReference(state);
}
public Atom(Object state, IPersistentMap meta){
super(meta);
this.state = new AtomicReference(state);
}
public Object deref(){
return state.get();
}
public Object swap(IFn f) {
for(; ;)
{
Object v = deref();
Object newv = f.invoke(v);
validate(newv);
if(state.compareAndSet(v, newv))
{
notifyWatches(v, newv);
return newv;
}
}
}
public Object swap(IFn f, Object arg) {
for(; ;)
{
Object v = deref();
Object newv = f.invoke(v, arg);
validate(newv);
if(state.compareAndSet(v, newv))
{
notifyWatches(v, newv);
return newv;
}
}
}
public Object swap(IFn f, Object arg1, Object arg2) {
for(; ;)
{
Object v = deref();
Object newv = f.invoke(v, arg1, arg2);
validate(newv);
if(state.compareAndSet(v, newv))
{
notifyWatches(v, newv);
return newv;
}
}
}
public Object swap(IFn f, Object x, Object y, ISeq args) {
for(; ;)
{
Object v = deref();
Object newv = f.applyTo(RT.listStar(v, x, y, args));
validate(newv);
if(state.compareAndSet(v, newv))
{
notifyWatches(v, newv);
return newv;
}
}
}
public boolean compareAndSet(Object oldv, Object newv){
validate(newv);
boolean ret = state.compareAndSet(oldv, newv);
if(ret)
notifyWatches(oldv, newv);
return ret;
}
public Object reset(Object newval){
Object oldval = state.get();
validate(newval);
state.set(newval);
notifyWatches(oldval, newval);
return newval;
}
}
================================================
FILE: src/jvm/clojure/lang/BigInt.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* chouser Jun 23, 2010 */
package clojure.lang;
import java.math.BigInteger;
import java.math.BigDecimal;
public final class BigInt extends Number implements IHashEq{
final public long lpart;
final public BigInteger bipart;
final public static BigInt ZERO = new BigInt(0,null);
final public static BigInt ONE = new BigInt(1,null);
//must follow Long
public int hashCode(){
if(bipart == null)
return (int) (this.lpart ^ (this.lpart >>> 32));
return Util.hash(bipart);
}
public int hasheq(){
if(bipart == null)
return Murmur3.hashLong(lpart);
return Util.hash(bipart);
}
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj instanceof BigInt)
{
BigInt o = (BigInt) obj;
if(bipart == null)
return o.bipart == null && this.lpart == o.lpart;
return o.bipart != null && this.bipart.equals(o.bipart);
}
return false;
}
private BigInt(long lpart, BigInteger bipart){
this.lpart = lpart;
this.bipart = bipart;
}
public static BigInt fromBigInteger(BigInteger val){
if(val.bitLength() < 64)
return new BigInt(val.longValue(), null);
else
return new BigInt(0, val);
}
public static BigInt fromLong(long val){
return new BigInt(val, null);
}
public BigInteger toBigInteger(){
if(bipart == null)
return BigInteger.valueOf(lpart);
else
return bipart;
}
public BigDecimal toBigDecimal(){
if(bipart == null)
return BigDecimal.valueOf(lpart);
else
return new BigDecimal(bipart);
}
///// java.lang.Number:
public int intValue(){
if(bipart == null)
return (int) lpart;
else
return bipart.intValue();
}
public long longValue(){
if(bipart == null)
return lpart;
else
return bipart.longValue();
}
public float floatValue(){
if(bipart == null)
return lpart;
else
return bipart.floatValue();
}
public double doubleValue(){
if(bipart == null)
return lpart;
else
return bipart.doubleValue();
}
public byte byteValue(){
if(bipart == null)
return (byte) lpart;
else
return bipart.byteValue();
}
public short shortValue(){
if(bipart == null)
return (short) lpart;
else
return bipart.shortValue();
}
public static BigInt valueOf(long val){
return new BigInt(val, null);
}
public String toString(){
if(bipart == null)
return String.valueOf(lpart);
return bipart.toString();
}
public int bitLength(){
return toBigInteger().bitLength();
}
public BigInt add(BigInt y) {
if ((bipart == null) && (y.bipart == null)) {
long ret = lpart + y.lpart;
if ((ret ^ lpart) >= 0 || (ret ^ y.lpart) >= 0)
return BigInt.valueOf(ret);
}
return BigInt.fromBigInteger(this.toBigInteger().add(y.toBigInteger()));
}
public BigInt multiply(BigInt y) {
if ((bipart == null) && (y.bipart == null)) {
long ret = lpart * y.lpart;
if (y.lpart == 0 ||
(ret / y.lpart == lpart && lpart != Long.MIN_VALUE))
return BigInt.valueOf(ret);
}
return BigInt.fromBigInteger(this.toBigInteger().multiply(y.toBigInteger()));
}
public BigInt quotient(BigInt y) {
if ((bipart == null) && (y.bipart == null)) {
return BigInt.valueOf(lpart / y.lpart);
}
return BigInt.fromBigInteger(this.toBigInteger().divide(y.toBigInteger()));
}
public BigInt remainder(BigInt y) {
if ((bipart == null) && (y.bipart == null)) {
return BigInt.valueOf(lpart % y.lpart);
}
return BigInt.fromBigInteger(this.toBigInteger().remainder(y.toBigInteger()));
}
public boolean lt(BigInt y) {
if ((bipart == null) && (y.bipart == null)) {
return lpart < y.lpart;
}
return this.toBigInteger().compareTo(y.toBigInteger()) < 0;
}
}
================================================
FILE: src/jvm/clojure/lang/Binding.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public class Binding{
public T val;
public final Binding rest;
public Binding(T val){
this.val = val;
this.rest = null;
}
public Binding(T val, Binding rest){
this.val = val;
this.rest = rest;
}
}
================================================
FILE: src/jvm/clojure/lang/Box.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 27, 2006 8:40:19 PM */
package clojure.lang;
public class Box{
public Object val;
public Box(Object val){
this.val = val;
}
}
================================================
FILE: src/jvm/clojure/lang/ChunkBuffer.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich May 26, 2009 */
package clojure.lang;
final public class ChunkBuffer implements Counted{
Object[] buffer;
int end;
public ChunkBuffer(int capacity){
buffer = new Object[capacity];
end = 0;
}
public void add(Object o){
buffer[end++] = o;
}
public IChunk chunk(){
ArrayChunk ret = new ArrayChunk(buffer, 0, end);
buffer = null;
return ret;
}
public int count(){
return end;
}
}
================================================
FILE: src/jvm/clojure/lang/ChunkedCons.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich May 25, 2009 */
package clojure.lang;
final public class ChunkedCons extends ASeq implements IChunkedSeq{
final IChunk chunk;
final ISeq _more;
ChunkedCons(IPersistentMap meta, IChunk chunk, ISeq more){
super(meta);
this.chunk = chunk;
this._more = more;
}
public ChunkedCons(IChunk chunk, ISeq more){
this(null,chunk, more);
}
public Obj withMeta(IPersistentMap meta){
if(meta != _meta)
return new ChunkedCons(meta, chunk, _more);
return this;
}
public Object first(){
return chunk.nth(0);
}
public ISeq next(){
if(chunk.count() > 1)
return new ChunkedCons(chunk.dropFirst(), _more);
return chunkedNext();
}
public ISeq more(){
if(chunk.count() > 1)
return new ChunkedCons(chunk.dropFirst(), _more);
if(_more == null)
return PersistentList.EMPTY;
return _more;
}
public IChunk chunkedFirst(){
return chunk;
}
public ISeq chunkedNext(){
return chunkedMore().seq();
}
public ISeq chunkedMore(){
if(_more == null)
return PersistentList.EMPTY;
return _more;
}
}
================================================
FILE: src/jvm/clojure/lang/Compile.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Map;
import java.io.IOException;
// Compiles libs and generates class files stored within the directory
// named by the Java System property "clojure.compile.path". Arguments are
// strings naming the libs to be compiled. The libs and compile-path must
// all be within CLASSPATH.
public class Compile {
private static final String PATH_PROP = "clojure.compile.path";
private static final String REFLECTION_WARNING_PROP = "clojure.compile.warn-on-reflection";
private static final String UNCHECKED_MATH_PROP = "clojure.compile.unchecked-math";
private static final Var compile_path = RT.var("clojure.core", "*compile-path*");
private static final Var compile = RT.var("clojure.core", "compile");
private static final Var warn_on_reflection = RT.var("clojure.core", "*warn-on-reflection*");
private static final Var unchecked_math = RT.var("clojure.core", "*unchecked-math*");
private static Keyword sourceOutputKey = Keyword.intern("clojure.compiler.source-output");
public static void main(String[] args) throws Exception {
OutputStreamWriter out = (OutputStreamWriter) RT.OUT.deref();
PrintWriter err = RT.errPrintWriter();
String path = System.getProperty(PATH_PROP);
int count = args.length;
if (path == null) {
err.println("ERROR: Must set system property " + PATH_PROP
+ "\nto the location for compiled .class files."
+ "\nThis directory must also be on your CLASSPATH.");
System.exit(1);
}
boolean warnOnReflection = System.getProperty(REFLECTION_WARNING_PROP,
"false").equals("true");
boolean uncheckedMath = System.getProperty(UNCHECKED_MATH_PROP, "false")
.equals("true");
String sourceOutput = "target/gen"; //(String) (compilerOptions.containsKey(sourceOutputKey) ? compilerOptions.valAt(sourceOutputKey ) : "target/gen");
new File(sourceOutput).mkdirs();
try {
Var.pushThreadBindings(RT.map(clojure.lang.Compiler.SOURCE_GEN_PATH,
sourceOutput, compile_path, path, warn_on_reflection,
warnOnReflection, unchecked_math, uncheckedMath));
for (String lib : args) {
out.write("Compiling " + lib + " to " + path + "\n");
out.flush();
compile.invoke(Symbol.intern(lib));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Var.popThreadBindings();
try {
out.flush();
} catch (IOException e) {
e.printStackTrace(err);
}
System.exit(0);
}
}
}
================================================
FILE: src/jvm/clojure/lang/Compiler.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Aug 21, 2007 */
package clojure.lang;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.j2objc.annotations.ReflectionSupport.Level;
import clojure.main;
import clojure.asm.Attribute;
import clojure.asm.ByteVector;
import clojure.asm.ClassVisitor;
import clojure.asm.ClassWriter;
import clojure.asm.FieldVisitor;
import clojure.asm.Label;
import clojure.asm.MethodVisitor;
import clojure.asm.Opcodes;
import clojure.asm.Type;
import clojure.asm.commons.GeneratorAdapter;
import clojure.asm.commons.Method;
public class Compiler implements Opcodes {
static final String DOLLAR = "_";
static final Symbol DEF = Symbol.intern("def");
static final Symbol LOOP = Symbol.intern("loop*");
static final Symbol RECUR = Symbol.intern("recur");
static final Symbol IF = Symbol.intern("if");
static final Symbol LET = Symbol.intern("let*");
static final Symbol LETFN = Symbol.intern("letfn*");
static final Symbol DO = Symbol.intern("do");
static final Symbol FN = Symbol.intern("fn*");
static final Symbol FNONCE = (Symbol) Symbol.intern("fn*").withMeta(
RT.map(Keyword.intern(null, "once"), RT.T));
static final Symbol QUOTE = Symbol.intern("quote");
static final Symbol THE_VAR = Symbol.intern("var");
static final Symbol DOT = Symbol.intern(".");
static final Symbol ASSIGN = Symbol.intern("set!");
// static final Symbol TRY_FINALLY = Symbol.intern("try-finally");
static final Symbol TRY = Symbol.intern("try");
static final Symbol CATCH = Symbol.intern("catch");
static final Symbol FINALLY = Symbol.intern("finally");
static final Symbol THROW = Symbol.intern("throw");
static final Symbol MONITOR_ENTER = Symbol.intern("monitor-enter");
static final Symbol MONITOR_EXIT = Symbol.intern("monitor-exit");
static final Symbol IMPORT = Symbol.intern("clojure.core", "import*");
// static final Symbol INSTANCE = Symbol.intern("instance?");
static final Symbol DEFTYPE = Symbol.intern("deftype*");
static final Symbol CASE = Symbol.intern("case*");
// static final Symbol THISFN = Symbol.intern("thisfn");
static final Symbol CLASS = Symbol.intern("Class");
static final Symbol NEW = Symbol.intern("new");
static final Symbol THIS = Symbol.intern("this");
static final Symbol REIFY = Symbol.intern("reify*");
// static final Symbol UNQUOTE = Symbol.intern("unquote");
// static final Symbol UNQUOTE_SPLICING = Symbol.intern("unquote-splicing");
// static final Symbol SYNTAX_QUOTE = Symbol.intern("clojure.core",
// "syntax-quote");
static final Symbol LIST = Symbol.intern("clojure.core", "list");
static final Symbol HASHMAP = Symbol.intern("clojure.core", "hash-map");
static final Symbol VECTOR = Symbol.intern("clojure.core", "vector");
static final Symbol IDENTITY = Symbol.intern("clojure.core", "identity");
static final Symbol _AMP_ = Symbol.intern("&");
static final Symbol ISEQ = Symbol.intern("clojure.lang.ISeq");
static final Keyword inlineKey = Keyword.intern(null, "inline");
static final Keyword inlineAritiesKey = Keyword
.intern(null, "inline-arities");
static final Keyword staticKey = Keyword.intern(null, "static");
static final Keyword arglistsKey = Keyword.intern(null, "arglists");
static final Symbol INVOKE_STATIC = Symbol.intern("invokeStatic");
static final Keyword volatileKey = Keyword.intern(null, "volatile");
static final Keyword implementsKey = Keyword.intern(null, "implements");
static final String COMPILE_STUB_PREFIX = "compile__stub";
static final Keyword protocolKey = Keyword.intern(null, "protocol");
static final Keyword onKey = Keyword.intern(null, "on");
static Keyword dynamicKey = Keyword.intern("dynamic");
static final Symbol NS = Symbol.intern("ns");
static final Symbol IN_NS = Symbol.intern("in-ns");
// static final Symbol IMPORT = Symbol.intern("import");
// static final Symbol USE = Symbol.intern("use");
// static final Symbol IFN = Symbol.intern("clojure.lang", "IFn");
static final public IPersistentMap specials = PersistentHashMap.create(DEF,
new DefExpr.Parser(), LOOP, new LetExpr.Parser(), RECUR,
new RecurExpr.Parser(), IF, new IfExpr.Parser(), CASE,
new CaseExpr.Parser(), LET, new LetExpr.Parser(), LETFN,
new LetFnExpr.Parser(), DO, new BodyExpr.Parser(), FN, null, QUOTE,
new ConstantExpr.Parser(), THE_VAR, new TheVarExpr.Parser(), IMPORT,
new ImportExpr.Parser(), DOT, new HostExpr.Parser(), ASSIGN,
new AssignExpr.Parser(), DEFTYPE, new NewInstanceExpr.DeftypeParser(),
REIFY,
new NewInstanceExpr.ReifyParser(),
// TRY_FINALLY, new TryFinallyExpr.Parser(),
TRY, new TryExpr.Parser(), THROW, new ThrowExpr.Parser(), MONITOR_ENTER,
new MonitorEnterExpr.Parser(), MONITOR_EXIT,
new MonitorExitExpr.Parser(),
// INSTANCE, new InstanceExpr.Parser(),
// IDENTICAL, new IdenticalExpr.Parser(),
// THISFN, null,
CATCH, null, FINALLY, null,
// CLASS, new ClassExpr.Parser(),
NEW, new NewExpr.Parser(),
// UNQUOTE, null,
// UNQUOTE_SPLICING, null,
// SYNTAX_QUOTE, null,
_AMP_, null);
private static final int MAX_POSITIONAL_ARITY = 20;
private static final Type OBJECT_TYPE;
// private static final Type KEYWORD_TYPE = Type.getType(Keyword.class);
private static final Type VAR_TYPE = Type.getType(Var.class);
private static final Type SYMBOL_TYPE = Type.getType(Symbol.class);
// private static final Type NUM_TYPE = Type.getType(Num.class);
private static final Type IFN_TYPE = Type.getType(IFn.class);
// private static final Type AFUNCTION_TYPE = Type.getType(AFunction.class);
private static final Type RT_TYPE = Type.getType(RT.class);
private static final Type NUMBERS_TYPE = Type.getType(Numbers.class);
final static Type CLASS_TYPE = Type.getType(Class.class);
final static Type NS_TYPE = Type.getType(Namespace.class);
final static Type UTIL_TYPE = Type.getType(Util.class);
final static Type REFLECTOR_TYPE = Type.getType(Reflector.class);
final static Type THROWABLE_TYPE = Type.getType(Throwable.class);
final static Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class);
final static Type IPERSISTENTMAP_TYPE = Type.getType(IPersistentMap.class);
final static Type IOBJ_TYPE = Type.getType(IObj.class);
private static final Type[][] ARG_TYPES;
// private static final Type[] EXCEPTION_TYPES =
// {Type.getType(Exception.class)};
private static final Type[] EXCEPTION_TYPES = {};
static final public IPersistentMap CHAR_MAP = PersistentHashMap.create(
'-',
"_",
// '.', "_DOT_",
':', "_COLON_", '+', "_PLUS_", '>', "_GT_", '<', "_LT_", '=', "_EQ_",
'~', "_TILDE_", '!', "_BANG_", '@', "_CIRCA_", '#', "_SHARP_", '\'',
"_SINGLEQUOTE_", '"', "_DOUBLEQUOTE_", '%', "_PERCENT_", '^', "_CARET_",
'&', "_AMPERSAND_", '*', "_STAR_", '|', "_BAR_", '{', "_LBRACE_", '}',
"_RBRACE_", '[', "_LBRACK_", ']', "_RBRACK_", '/', "_SLASH_", '\\',
"_BSLASH_", '?', "_QMARK_");
static final public IPersistentMap DEMUNGE_MAP;
static final public Pattern DEMUNGE_PATTERN;
static {
OBJECT_TYPE = Type.getType(Object.class);
ARG_TYPES = new Type[MAX_POSITIONAL_ARITY + 2][];
for (int i = 0; i <= MAX_POSITIONAL_ARITY; ++i) {
Type[] a = new Type[i];
for (int j = 0; j < i; j++)
a[j] = OBJECT_TYPE;
ARG_TYPES[i] = a;
}
Type[] a = new Type[MAX_POSITIONAL_ARITY + 1];
for (int j = 0; j < MAX_POSITIONAL_ARITY; j++)
a[j] = OBJECT_TYPE;
a[MAX_POSITIONAL_ARITY] = Type.getType("[Ljava/lang/Object;");
ARG_TYPES[MAX_POSITIONAL_ARITY + 1] = a;
}
// static final public Var RUNTIME = Var.intern(
// Namespace.findOrCreate(Symbol.intern("clojure.core")),
// Symbol.intern("*runtime*"), Boolean.FALSE).setDynamic();
static final public Var STOP_EMIT_SOURCE = Var.create(false).setDynamic();
// symbol->localbinding
static final public Var LOCAL_ENV = Var.create(null).setDynamic();
// vector
static final public Var LOOP_LOCALS = Var.create().setDynamic();
// Label
static final public Var LOOP_LABEL = Var.create().setDynamic();
// Return type of method
static final public Var RETURN_TYPE = Var.create().setDynamic();
// vector
static final public Var CONSTANTS = Var.create().setDynamic();
// IdentityHashMap
static final public Var CONSTANT_IDS = Var.create().setDynamic();
// vector
static final public Var KEYWORD_CALLSITES = Var.create().setDynamic();
// vector
static final public Var PROTOCOL_CALLSITES = Var.create().setDynamic();
// set
static final public Var VAR_CALLSITES = Var.create().setDynamic();
// keyword->constid
static final public Var KEYWORDS = Var.create().setDynamic();
// var->constid
static final public Var VARS = Var.create().setDynamic();
// FnFrame
static final public Var METHOD = Var.create(null).setDynamic();
// null or not
static final public Var IN_CATCH_FINALLY = Var.create(null).setDynamic();
static final public Var NO_RECUR = Var.create(null).setDynamic();
// DynamicClassLoader
static final public Var LOADER = Var.create().setDynamic();
static final public Var NEXT_ID = Var.create(new Atom(1)).setDynamic();
// String
static final public Var SOURCE = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*source-path*"), "NO_SOURCE_FILE").setDynamic();
// String
static final public Var SOURCE_PATH = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*file*"), "NO_SOURCE_PATH").setDynamic();
static final public Var SOURCE_WRITER = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*source-writer*"), null).setDynamic();
// String
static final public Var COMPILE_PATH = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*compile-path*"), null).setDynamic();
// String
static final public Var SOURCE_GEN_PATH = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*source-gen-path*"), null).setDynamic();
// boolean
static final public Var COMPILE_FILES = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*compile-files*"), Boolean.FALSE).setDynamic();
static final public Var INSTANCE = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("instance?"));
static final public Var ADD_ANNOTATIONS = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("add-annotations"));
static final public Keyword disableLocalsClearingKey = Keyword
.intern("disable-locals-clearing");
static final public Keyword elideMetaKey = Keyword.intern("elide-meta");
static final public Var COMPILER_OPTIONS;
public static void emitSource(String source) {
if (Boolean.TRUE.equals(STOP_EMIT_SOURCE.deref())) {
return;
}
if (source != null && source.endsWith(";;")) {
new RuntimeException(source).printStackTrace();
}
if (source == null) {
new RuntimeException().printStackTrace();
return; // TODO Handle error
}
if (source != null && (source.isEmpty() || "null;".equals(source))) {
return;
}
SourceWriter sc = (SourceWriter) SOURCE_WRITER.deref();
if (sc != null) {
source = source.replaceAll(COMPILE_STUB_PREFIX + ".", ""); // TODO ?
sc.println(source);
}
}
public static int nextScopedID() {
Atom n = (Atom) NEXT_ID.deref();
Integer i = (Integer) n.deref();
n.reset(i+1);
return i;
}
public static void emitSource() {
SourceWriter sc = (SourceWriter) SOURCE_WRITER.deref();
if (sc != null) {
sc.println();
}
}
public static void tab() {
SourceWriter sc = (SourceWriter) SOURCE_WRITER.deref();
if (sc != null) {
sc.tab();
}
}
public static void untab() {
SourceWriter sc = (SourceWriter) SOURCE_WRITER.deref();
if (sc != null) {
sc.untab();
}
}
static public Object getCompilerOption(Keyword k) {
return RT.get(COMPILER_OPTIONS.deref(), k);
}
static {
Object compilerOptions = null;
for (Map.Entry e : System.getProperties().entrySet()) {
String name = (String) e.getKey();
String v = (String) e.getValue();
if (name.startsWith("clojure.compiler.")) {
compilerOptions = RT.assoc(compilerOptions,
RT.keyword(null, name.substring(1 + name.lastIndexOf('.'))),
RT.readString(v));
}
}
COMPILER_OPTIONS = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*compiler-options*"), compilerOptions).setDynamic();
}
static Object elideMeta(Object m) {
Collection elides = (Collection) getCompilerOption(elideMetaKey);
if (elides != null) {
for (Object k : elides) {
// System.out.println("Eliding:" + k + " : " + RT.get(m, k));
m = RT.dissoc(m, k);
}
// System.out.println("Remaining: " + RT.keys(m));
}
return m;
}
// Integer
static final public Var LINE = Var.create(0).setDynamic();
static final public Var COLUMN = Var.create(0).setDynamic();
static int lineDeref() {
return ((Number) LINE.deref()).intValue();
}
static int columnDeref() {
return ((Number) COLUMN.deref()).intValue();
}
// Integer
static final public Var LINE_BEFORE = Var.create(0).setDynamic();
static final public Var COLUMN_BEFORE = Var.create(0).setDynamic();
static final public Var LINE_AFTER = Var.create(0).setDynamic();
static final public Var COLUMN_AFTER = Var.create(0).setDynamic();
// Integer
static final public Var NEXT_LOCAL_NUM = Var.create(0).setDynamic();
// Integer
static final public Var RET_LOCAL_NUM = Var.create().setDynamic();
static final public Var COMPILE_STUB_SYM = Var.create(null).setDynamic();
static final public Var COMPILE_STUB_CLASS = Var.create(null).setDynamic();
// PathNode chain
static final public Var CLEAR_PATH = Var.create(null).setDynamic();
// tail of PathNode chain
static final public Var CLEAR_ROOT = Var.create(null).setDynamic();
// LocalBinding -> Set
static final public Var CLEAR_SITES = Var.create(null).setDynamic();
public enum C {
STATEMENT, // value ignored
EXPRESSION, // value required
RETURN, // tail position relative to enclosing recur frame
EVAL
}
private class Recur {
};
static final public Class RECUR_CLASS = Recur.class;
interface Expr {
Object eval();
String emit(C context, ObjExpr objx, GeneratorAdapter gen);
boolean hasJavaClass();
Class getJavaClass();
}
public static abstract class UntypedExpr implements Expr {
public Class getJavaClass() {
throw new IllegalArgumentException("Has no Java class");
}
public boolean hasJavaClass() {
return false;
}
}
interface IParser {
Expr parse(C context, Object form);
}
static boolean isSpecial(Object sym) {
return specials.containsKey(sym);
}
public static String printClass(Object c) {
if (c instanceof Class) {
return ((Class) c).getCanonicalName();
} else if (c instanceof Type) {
return ((Type) c).getClassName();
} else if (c instanceof String) {
return c.toString();
}
throw new RuntimeException("printClass doesn't support: "
+ c.getClass().getCanonicalName());
}
static Symbol resolveSymbol(Symbol sym) {
// already qualified or classname?
if (sym.name.indexOf('.') > 0)
return sym;
if (sym.ns != null) {
Namespace ns = namespaceFor(sym);
if (ns == null || (ns.name.name == null ? sym.ns == null : ns.name.name.equals(sym.ns)))
return sym;
return Symbol.intern(ns.name.name, sym.name);
}
Object o = currentNS().getMapping(sym);
if (o == null)
return Symbol.intern(currentNS().name.name, sym.name);
else if (o instanceof Class)
return Symbol.intern(null, ((Class) o).getName());
else if (o instanceof Var) {
Var v = (Var) o;
return Symbol.intern(v.ns.name.name, v.sym.name);
}
return null;
}
static class DefExpr implements Expr {
public final Var var;
public final Expr init;
public final Expr meta;
public final boolean initProvided;
public final boolean emitOnInit;
public final boolean isDynamic;
public final boolean shadowsCoreMapping;
public final String source;
public final int line;
public final int column;
final static Method bindRootMethod = Method
.getMethod("void bindRoot(Object)");
final static Method setTagMethod = Method
.getMethod("void setTag(clojure.lang.Symbol)");
final static Method setMetaMethod = Method
.getMethod("void setMeta(clojure.lang.IPersistentMap)");
final static Method setDynamicMethod = Method
.getMethod("clojure.lang.Var setDynamic(boolean)");
final static Method symintern = Method
.getMethod("clojure.lang.Symbol intern(String, String)");
final static Method internVar = Method.getMethod("clojure.lang.Var refer(clojure.lang.Symbol, clojure.lang.Var)");
public DefExpr(C c, String source, int line, int column, Var var,
Expr init, Expr meta, boolean initProvided, boolean isDynamic,
boolean shadowsCoreMapping, boolean isDeclared) {
this.source = source;
this.line = line;
this.column = column;
this.var = var;
this.init = init;
this.meta = meta;
this.isDynamic = isDynamic;
this.shadowsCoreMapping = shadowsCoreMapping;
this.initProvided = initProvided;
this.emitOnInit = C.RETURN == c || C.EXPRESSION == c || !(init instanceof FnExpr);
}
// private boolean includesExplicitMetadata(MapExpr expr) {
// for (int i = 0; i < expr.keyvals.count(); i += 2) {
// Keyword k = ((KeywordExpr) expr.keyvals.nth(i)).k;
// if ((k != RT.FILE_KEY) && (k != RT.DECLARED_KEY) && (k != RT.LINE_KEY)
// && (k != RT.COLUMN_KEY))
// return true;
// }
// return false;
// }
public Object eval() {
try {
if (initProvided) {
// if(init instanceof FnExpr && ((FnExpr) init).closes.count()==0)
// var.bindRoot(new FnLoaderThunk((FnExpr) init,var));
// else
var.bindRoot(init.eval());
}
if (meta != null) {
var.setMeta((IPersistentMap) meta.eval());
}
return var.setDynamic(isDynamic);
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException(source, line, column, e);
else
throw (CompilerException) e;
}
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (emitOnInit) {
String constant = objx.emitVar(gen, var);
if (shadowsCoreMapping)
{
gen.dup();
gen.getField(VAR_TYPE, "ns", NS_TYPE);
gen.swap();
gen.dup();
gen.getField(VAR_TYPE, "sym", SYMBOL_TYPE);
gen.swap();
gen.invokeVirtual(NS_TYPE, internVar);
constant = constant + ".ns.refer(" + constant + ".sym, " + constant + ")";
}
if (isDynamic) {
gen.push(isDynamic);
gen.invokeVirtual(VAR_TYPE, setDynamicMethod);
emitSource(constant + ".setDynamic(true);");
}
if (meta != null) {
if (initProvided || true)// includesExplicitMetadata((MapExpr) meta))
{
gen.dup();
String val = meta.emit(C.EXPRESSION, objx, gen);
gen.checkCast(IPERSISTENTMAP_TYPE);
gen.invokeVirtual(VAR_TYPE, setMetaMethod);
emitSource(constant + ".setMeta((IPersistentMap)" + val + ");");
}
}
if (initProvided) {
gen.dup();
String val;
if (init instanceof FnExpr) {
val = ((FnExpr) init).emitForDefn(objx, gen);
} else
val = init.emit(C.EXPRESSION, objx, gen);
gen.invokeVirtual(VAR_TYPE, bindRootMethod);
emitSource(constant + ".bindRoot(" + val + ");");
}
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, constant);
}
} else {
return "";
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return Var.class;
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
// (def x) or (def x initexpr) or (def x "docstring" initexpr)
String docstring = null;
if (RT.count(form) == 4 && (RT.third(form) instanceof String)) {
docstring = (String) RT.third(form);
form = RT.list(RT.first(form), RT.second(form), RT.fourth(form));
}
if (RT.count(form) > 3)
throw Util.runtimeException("Too many arguments to def");
else if (RT.count(form) < 2)
throw Util.runtimeException("Too few arguments to def");
else if (!(RT.second(form) instanceof Symbol))
throw Util.runtimeException("First argument to def must be a Symbol");
Symbol sym = (Symbol) RT.second(form);
Var v = lookupVar(sym, true);
if (v == null)
throw Util
.runtimeException("Can't refer to qualified var that doesn't exist");
boolean shadowsCoreMapping = false;
if (!v.ns.equals(currentNS())) {
if (sym.ns == null) {
v = currentNS().intern(sym);
shadowsCoreMapping = true;
registerVar(v);
}
// throw Util.runtimeException("Name conflict, can't def " + sym +
// " because namespace: " + currentNS().name +
// " refers to:" + v);
else
throw Util
.runtimeException("Can't create defs outside of current ns");
}
IPersistentMap mm = sym.meta();
boolean isDynamic = RT.booleanCast(RT.get(mm, dynamicKey));
boolean isDeclared = RT.booleanCast(RT.get(mm,
Keyword.intern("declared")));
if (isDynamic)
v.setDynamic();
if (!isDynamic && sym.name.startsWith("*") && sym.name.endsWith("*")
&& sym.name.length() > 2) {
RT.errPrintWriter()
.format(
"Warning: %1$s not declared dynamic and thus is not dynamically rebindable, "
+ "but its name suggests otherwise. Please either indicate ^:dynamic %1$s or change the name. (%2$s:%3$d)\n",
sym, SOURCE_PATH.get(), LINE.get());
}
if (RT.booleanCast(RT.get(mm, arglistsKey))) {
IPersistentMap vm = v.meta();
// vm = (IPersistentMap) RT.assoc(vm,staticKey,RT.T);
vm = (IPersistentMap) RT.assoc(vm, Var.macroKey,
mm.valAt(Var.macroKey));
// drop quote
vm = (IPersistentMap) RT.assoc(vm, arglistsKey,
RT.second(mm.valAt(arglistsKey)));
v.setMeta(vm);
}
Object source_path = SOURCE_PATH.get();
source_path = source_path == null ? "NO_SOURCE_FILE" : source_path;
mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get())
.assoc(RT.COLUMN_KEY, COLUMN.get()).assoc(RT.FILE_KEY, source_path);
if (docstring != null)
mm = (IPersistentMap) RT.assoc(mm, RT.DOC_KEY, docstring);
// mm = mm.without(RT.DOC_KEY)
// .without(Keyword.intern(null, "arglists"))
// .without(RT.FILE_KEY)
// .without(RT.LINE_KEY)
// .without(RT.COLUMN_KEY)
// .without(Keyword.intern(null, "ns"))
// .without(Keyword.intern(null, "name"))
// .without(Keyword.intern(null, "added"))
// .without(Keyword.intern(null, "static"));
mm = (IPersistentMap) elideMeta(mm);
Expr meta = null;
Expr init = analyze(
context == C.EVAL ? context : C.EXPRESSION,
RT.third(form),
v.sym.name,
(C.RETURN == context || C.EXPRESSION == context) ? null : RT
.vector(v, mm, isDynamic));
if ((C.RETURN == context || C.EXPRESSION == context)
|| (!isDeclared && !(init instanceof FnExpr))) {
meta = mm.count() == 0 ? null : analyze(context == C.EVAL ? context
: C.EXPRESSION, mm);
}
return new DefExpr(context, (String) SOURCE.deref(), lineDeref(),
columnDeref(), v, init, meta, RT.count(form) == 3, isDynamic, shadowsCoreMapping,
isDeclared);
}
}
}
public static class AssignExpr implements Expr {
public final AssignableExpr target;
public final Expr val;
public AssignExpr(AssignableExpr target, Expr val) {
this.target = target;
this.val = val;
}
public Object eval() {
return target.evalAssign(val);
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
return target.emitAssign(context, objx, gen, val);
}
public boolean hasJavaClass() {
return val.hasJavaClass();
}
public Class getJavaClass() {
return val.getJavaClass();
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
if (RT.length(form) != 3)
throw new IllegalArgumentException(
"Malformed assignment, expecting (set! target val)");
Expr target = analyze(C.EXPRESSION, RT.second(form));
if (!(target instanceof AssignableExpr))
throw new IllegalArgumentException("Invalid assignment target");
return new AssignExpr((AssignableExpr) target, analyze(C.EXPRESSION,
RT.third(form)));
}
}
}
public static class VarExpr implements Expr, AssignableExpr {
public final Var var;
public final Object tag;
final static Method getMethod = Method.getMethod("Object get()");
final static Method setMethod = Method.getMethod("Object set(Object)");
public VarExpr(Var var, Symbol tag) {
this.var = var;
this.tag = tag != null ? tag : var.getTag();
}
public Object eval() {
return var.deref();
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = objx.emitVarValue(gen, var);
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, val);
}
}
public boolean hasJavaClass() {
return tag != null;
}
public Class getJavaClass() {
return HostExpr.tagToClass(tag);
}
public Object evalAssign(Expr val) {
return var.set(val.eval());
}
public String emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val) {
String to = objx.emitVar(gen, var);
String value = val.emit(C.EXPRESSION, objx, gen);
gen.invokeVirtual(VAR_TYPE, setMethod);
if (context == C.STATEMENT)
gen.pop();
return wrap(context, to + ".set(" + value + ")");
}
}
public static class TheVarExpr implements Expr {
public final Var var;
public TheVarExpr(Var var) {
this.var = var;
}
public Object eval() {
return var;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = objx.emitVar(gen, var);
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, val);
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return Var.class;
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
Symbol sym = (Symbol) RT.second(form);
Var v = lookupVar(sym, false);
if (v != null)
return new TheVarExpr(v);
throw Util.runtimeException("Unable to resolve var: " + sym
+ " in this context");
}
}
}
public static class KeywordExpr extends LiteralExpr {
public final Keyword k;
public KeywordExpr(Keyword k) {
this.k = k;
}
Object val() {
return k;
}
public Object eval() {
return k;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = objx.emitKeyword(gen, k);
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, val);
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return Keyword.class;
}
}
public static class ImportExpr implements Expr {
public final String c;
final static Method forNameMethod = Method
.getMethod("Class classForNameNonLoading(String)");
final static Method importClassMethod = Method
.getMethod("Class importClass(Class)");
final static Method derefMethod = Method.getMethod("Object deref()");
public ImportExpr(String c) {
this.c = c;
}
public Object eval() {
Namespace ns = (Namespace) RT.CURRENT_NS.deref();
ns.importClass(RT.classForNameNonLoading(c));
return null;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
gen.getStatic(RT_TYPE, "CURRENT_NS", VAR_TYPE);
gen.invokeVirtual(VAR_TYPE, derefMethod);
gen.checkCast(NS_TYPE);
gen.push(c);
gen.invokeStatic(RT_TYPE, forNameMethod);
gen.invokeVirtual(NS_TYPE, importClassMethod);
// TODO proper fix
String className = c;
try {
Class cl = Class.forName(className);
if (cl.getEnclosingClass() != null) {
className = className.replaceAll("\\$", ".");
}
} catch (Exception e) {
}
emitSource("((Namespace)RT.CURRENT_NS.deref()).importClass("
+ printClass(className) + ".class);");
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, "null");
}
}
public boolean hasJavaClass() {
return false;
}
public Class getJavaClass() {
throw new IllegalArgumentException("ImportExpr has no Java class");
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
return new ImportExpr((String) RT.second(form));
}
}
}
public static abstract class LiteralExpr implements Expr {
abstract Object val();
public Object eval() {
return val();
}
}
static interface AssignableExpr {
Object evalAssign(Expr val);
String emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val);
}
static public interface MaybePrimitiveExpr extends Expr {
public boolean canEmitPrimitive();
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen);
}
static public abstract class HostExpr implements Expr, MaybePrimitiveExpr {
final static Type BOOLEAN_TYPE = Type.getType(Boolean.class);
final static Type CHAR_TYPE = Type.getType(Character.class);
final static Type INTEGER_TYPE = Type.getType(Integer.class);
final static Type LONG_TYPE = Type.getType(Long.class);
final static Type FLOAT_TYPE = Type.getType(Float.class);
final static Type DOUBLE_TYPE = Type.getType(Double.class);
final static Type SHORT_TYPE = Type.getType(Short.class);
final static Type BYTE_TYPE = Type.getType(Byte.class);
final static Type NUMBER_TYPE = Type.getType(Number.class);
final static Method charValueMethod = Method.getMethod("char charValue()");
final static Method booleanValueMethod = Method
.getMethod("boolean booleanValue()");
final static Method charValueOfMethod = Method
.getMethod("Character valueOf(char)");
final static Method intValueOfMethod = Method
.getMethod("Integer valueOf(int)");
final static Method longValueOfMethod = Method
.getMethod("Long valueOf(long)");
final static Method floatValueOfMethod = Method
.getMethod("Float valueOf(float)");
final static Method doubleValueOfMethod = Method
.getMethod("Double valueOf(double)");
final static Method shortValueOfMethod = Method
.getMethod("Short valueOf(short)");
final static Method byteValueOfMethod = Method
.getMethod("Byte valueOf(byte)");
final static Method intValueMethod = Method.getMethod("int intValue()");
final static Method longValueMethod = Method.getMethod("long longValue()");
final static Method floatValueMethod = Method
.getMethod("float floatValue()");
final static Method doubleValueMethod = Method
.getMethod("double doubleValue()");
final static Method byteValueMethod = Method.getMethod("byte byteValue()");
final static Method shortValueMethod = Method
.getMethod("short shortValue()");
final static Method fromIntMethod = Method
.getMethod("clojure.lang.Num from(int)");
final static Method fromLongMethod = Method
.getMethod("clojure.lang.Num from(long)");
final static Method fromDoubleMethod = Method
.getMethod("clojure.lang.Num from(double)");
// *
public static String emitBoxReturn(ObjExpr objx, GeneratorAdapter gen,
Class returnType, String val) {
if (returnType.isPrimitive()) {
if (returnType == boolean.class) {
Label falseLabel = gen.newLabel();
Label endLabel = gen.newLabel();
gen.ifZCmp(GeneratorAdapter.EQ, falseLabel);
gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);
gen.goTo(endLabel);
gen.mark(falseLabel);
gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
// NIL_EXPR.emit(C.EXPRESSION, fn, gen);
gen.mark(endLabel);
return "(" + val + " ? Boolean.TRUE : Boolean.FALSE)";
} else if (returnType == void.class) {
NIL_EXPR.emit(C.EXPRESSION, objx, gen);
emitSource(val + ";");
return "null";
} else if (returnType == char.class) {
gen.invokeStatic(CHAR_TYPE, charValueOfMethod);
return "Character.valueOf(" + val + ")";
} else {
if (returnType == int.class) {
gen.invokeStatic(INTEGER_TYPE, intValueOfMethod);
// gen.visitInsn(I2L);
// gen.invokeStatic(NUMBERS_TYPE,
// Method.getMethod("Number num(long)"));
return "Integer.valueOf(" + val + ")";
} else if (returnType == float.class) {
gen.invokeStatic(FLOAT_TYPE, floatValueOfMethod);
// gen.visitInsn(F2D);
// gen.invokeStatic(DOUBLE_TYPE, doubleValueOfMethod);
return "Float.valueOf(" + val + ")";
} else if (returnType == double.class) {
gen.invokeStatic(DOUBLE_TYPE, doubleValueOfMethod);
return "Double.valueOf(" + val + ")";
} else if (returnType == long.class) {
gen.invokeStatic(NUMBERS_TYPE, Method.getMethod("Number num(long)"));
return "Numbers.num(" + val + ")";
} else if (returnType == byte.class) {
gen.invokeStatic(BYTE_TYPE, byteValueOfMethod);
return "Byte.valueOf(" + val + ")";
} else if (returnType == short.class) {
gen.invokeStatic(SHORT_TYPE, shortValueOfMethod);
return "Short.valueOf(" + val + ")";
}
}
}
return "((" + printClass(returnType) + ")" + val + ")";
}
// */
public static String emitUnboxArg(ObjExpr objx, GeneratorAdapter gen,
Class paramType, String val) {
if (paramType.isPrimitive()) {
if (paramType == boolean.class) {
gen.checkCast(BOOLEAN_TYPE);
gen.invokeVirtual(BOOLEAN_TYPE, booleanValueMethod);
// Label falseLabel = gen.newLabel();
// Label endLabel = gen.newLabel();
// gen.ifNull(falseLabel);
// gen.push(1);
// gen.goTo(endLabel);
// gen.mark(falseLabel);
// gen.push(0);
// gen.mark(endLabel);
return "((Boolean)" + val + ").booleanValue()";
} else if (paramType == char.class) {
gen.checkCast(CHAR_TYPE);
gen.invokeVirtual(CHAR_TYPE, charValueMethod);
return "((Character)" + val + ").charValue()";
} else {
Method m = null;
gen.checkCast(NUMBER_TYPE);
if (RT.booleanCast(RT.UNCHECKED_MATH.deref())) {
if (paramType == int.class)
m = Method.getMethod("int uncheckedIntCast(Object)");
else if (paramType == float.class)
m = Method.getMethod("float uncheckedFloatCast(Object)");
else if (paramType == double.class)
m = Method.getMethod("double uncheckedDoubleCast(Object)");
else if (paramType == long.class)
m = Method.getMethod("long uncheckedLongCast(Object)");
else if (paramType == byte.class)
m = Method.getMethod("byte uncheckedByteCast(Object)");
else if (paramType == short.class)
m = Method.getMethod("short uncheckedShortCast(Object)");
} else {
if (paramType == int.class)
m = Method.getMethod("int intCast(Object)");
else if (paramType == float.class)
m = Method.getMethod("float floatCast(Object)");
else if (paramType == double.class)
m = Method.getMethod("double doubleCast(Object)");
else if (paramType == long.class)
m = Method.getMethod("long longCast(Object)");
else if (paramType == byte.class)
m = Method.getMethod("byte byteCast(Object)");
else if (paramType == short.class)
m = Method.getMethod("short shortCast(Object)");
}
gen.invokeStatic(RT_TYPE, m);
return "RT." + m.getName() + "(" + val + ")";
}
} else {
gen.checkCast(Type.getType(paramType));
return "((" + printClass(paramType) + ")" + val + ")";
}
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
// (. x fieldname-sym) or
// (. x 0-ary-method)
// (. x methodname-sym args+)
// (. x (methodname-sym args?))
if (RT.length(form) < 3)
throw new IllegalArgumentException(
"Malformed member expression, expecting (. target member ...)");
// determine static or instance
// static target must be symbol, either fully.qualified.Classname or
// Classname that has been imported
int line = lineDeref();
int column = columnDeref();
String source = (String) SOURCE.deref();
Class c = maybeClass(RT.second(form), false);
// at this point c will be non-null if static
Expr instance = null;
if (c == null)
instance = analyze(context == C.EVAL ? context : C.EXPRESSION,
RT.second(form));
boolean maybeField = RT.length(form) == 3
&& (RT.third(form) instanceof Symbol && !RT.third(form).equals(
Symbol.intern("getClass")));
if (maybeField && !(((Symbol) RT.third(form)).name.charAt(0) == '-')) {
Symbol sym = (Symbol) RT.third(form);
if (c != null)
maybeField = Reflector.getMethods(c, 0, munge(sym.name), true)
.size() == 0;
else if (instance != null && instance.hasJavaClass()
&& instance.getJavaClass() != null)
maybeField = Reflector.getMethods(instance.getJavaClass(), 0,
munge(sym.name), false).size() == 0;
}
if (maybeField) // field
{
Symbol sym = (((Symbol) RT.third(form)).name.charAt(0) == '-') ? Symbol
.intern(((Symbol) RT.third(form)).name.substring(1))
: (Symbol) RT.third(form);
Symbol tag = tagOf(form);
if (c != null) {
return new StaticFieldExpr(line, column, c, munge(sym.name), tag);
} else
return new InstanceFieldExpr(line, column, instance,
munge(sym.name), tag,
(((Symbol) RT.third(form)).name.charAt(0) == '-'));
} else {
ISeq call = (ISeq) ((RT.third(form) instanceof ISeq) ? RT.third(form)
: RT.next(RT.next(form)));
if (!(RT.first(call) instanceof Symbol))
throw new IllegalArgumentException("Malformed member expression");
Symbol sym = (Symbol) RT.first(call);
Symbol tag = tagOf(form);
PersistentVector args = PersistentVector.EMPTY;
for (ISeq s = RT.next(call); s != null; s = s.next())
args = args.cons(analyze(
context == C.EVAL ? context : C.EXPRESSION, s.first()));
if (c != null)
return new StaticMethodExpr(source, line, column, tag, c,
munge(sym.name), args);
else
return new InstanceMethodExpr(source, line, column, tag, instance,
munge(sym.name), args);
}
}
}
private static Class maybeClass(Object form, boolean stringOk) {
if (form instanceof Class)
return (Class) form;
Class c = null;
if (form instanceof Symbol) {
Symbol sym = (Symbol) form;
if (sym.ns == null) // if ns-qualified can't be classname
{
if (Util.equals(sym, COMPILE_STUB_SYM.get()))
return (Class) COMPILE_STUB_CLASS.get();
if (sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
c = RT.classForName(sym.name);
else {
Object o = currentNS().getMapping(Symbol.intern(sym.toString().replaceAll("-", "_")));
if (o instanceof Class)
c = (Class) o;
else if(LOCAL_ENV.deref() != null && ((java.util.Map)LOCAL_ENV.deref()).containsKey(form))
return null;
else {
try {
c = RT.classForName(sym.name);
} catch (Exception e) {
// aargh
// leave c set to null -> return null
}
}
}
}
} else if (stringOk && form instanceof String)
c = RT.classForName((String) form);
return c;
}
/*
* private static String maybeClassName(Object form, boolean stringOk){
* String className = null; if(form instanceof Symbol) { Symbol sym =
* (Symbol) form; if(sym.ns == null) //if ns-qualified can't be classname {
* if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') className =
* sym.name; else { IPersistentMap imports = (IPersistentMap) ((Var)
* RT.NS_IMPORTS.get()).get(); className = (String) imports.valAt(sym); } }
* } else if(stringOk && form instanceof String) className = (String) form;
* return className; }
*/
static Class tagToClass(Object tag) {
Class c = null;
if (tag instanceof Symbol) {
Symbol sym = (Symbol) tag;
if (sym.ns == null) // if ns-qualified can't be classname
{
if (sym.name.equals("objects"))
c = Object[].class;
else if (sym.name.equals("ints"))
c = int[].class;
else if (sym.name.equals("longs"))
c = long[].class;
else if (sym.name.equals("floats"))
c = float[].class;
else if (sym.name.equals("doubles"))
c = double[].class;
else if (sym.name.equals("chars"))
c = char[].class;
else if (sym.name.equals("shorts"))
c = short[].class;
else if (sym.name.equals("bytes"))
c = byte[].class;
else if (sym.name.equals("booleans"))
c = boolean[].class;
else if (sym.name.equals("int"))
c = Integer.TYPE;
else if (sym.name.equals("long"))
c = Long.TYPE;
else if (sym.name.equals("float"))
c = Float.TYPE;
else if (sym.name.equals("double"))
c = Double.TYPE;
else if (sym.name.equals("char"))
c = Character.TYPE;
else if (sym.name.equals("short"))
c = Short.TYPE;
else if (sym.name.equals("byte"))
c = Byte.TYPE;
else if (sym.name.equals("boolean"))
c = Boolean.TYPE;
}
}
if(c == null)
c = maybeClass(tag, true);
if (c != null)
return c;
throw new IllegalArgumentException("Unable to resolve classname: " + tag);
}
public static String tagToCanonical(Object tag) {
try {
return printClass(tagToClass(tag));
} catch (Exception e) {
return printClass(String.valueOf(tag));
}
}
}
static abstract class FieldExpr extends HostExpr {
}
static class InstanceFieldExpr extends FieldExpr implements AssignableExpr {
public final Expr target;
public final Class targetClass;
public final java.lang.reflect.Field field;
public final String fieldName;
public final int line;
public final int column;
public final Symbol tag;
public final boolean requireField;
final static Method invokeNoArgInstanceMember = Method
.getMethod("Object invokeNoArgInstanceMember(Object,String,boolean)");
final static Method setInstanceFieldMethod = Method
.getMethod("Object setInstanceField(Object,String,Object)");
public InstanceFieldExpr(int line, int column, Expr target,
String fieldName, Symbol tag, boolean requireField) {
this.target = target;
this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null;
this.field = targetClass != null ? Reflector.getField(targetClass,
fieldName, false) : null;
this.fieldName = fieldName;
this.line = line;
this.column = column;
this.tag = tag;
this.requireField = requireField;
if (field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) {
if (targetClass == null) {
RT.errPrintWriter()
.format(
"Reflection warning, %s:%d:%d - reference to field %s can't be resolved.\n",
SOURCE_PATH.deref(), line, column, fieldName);
} else {
RT.errPrintWriter()
.format(
"Reflection warning, %s:%d:%d - reference to field %s on %s can't be resolved.\n",
SOURCE_PATH.deref(), line, column, fieldName,
targetClass.getName());
}
}
}
public Object eval() {
return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName,
requireField);
}
public boolean canEmitPrimitive() {
return targetClass != null && field != null
&& Util.isPrimitive(field.getType());
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
if (targetClass != null && field != null) {
String value = target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.checkCast(getType(targetClass));
gen.getField(getType(targetClass), fieldName,
Type.getType(field.getType()));
return wrap(context, "((" + printClass(targetClass) + ")" + value
+ ")." + fieldName);
} else
throw new UnsupportedOperationException(
"Unboxed emit of unknown member");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (targetClass != null && field != null) {
String value = target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.checkCast(getType(targetClass));
gen.getField(getType(targetClass), fieldName,
Type.getType(field.getType()));
// if(context != C.STATEMENT)
String v = "((" + printClass(targetClass) + ")" + value + ")."
+ fieldName;
v = HostExpr.emitBoxReturn(objx, gen, field.getType(), v);
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, v);
}
} else {
String t = target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.push(fieldName);
gen.push(requireField);
gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember);
if (context == C.STATEMENT)
gen.pop();
return wrap(context, "Reflector.invokeNoArgInstanceMember(" + t
+ ", \"" + fieldName + "\")");
}
}
public boolean hasJavaClass() {
return field != null || tag != null;
}
public Class getJavaClass() {
return tag != null ? HostExpr.tagToClass(tag) : field.getType();
}
public Object evalAssign(Expr val) {
return Reflector.setInstanceField(target.eval(), fieldName, val.eval());
}
public String emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val) {
String ret;
if (targetClass != null && field != null) {
String t = target.emit(C.EXPRESSION, objx, gen);
gen.checkCast(Type.getType(targetClass));
String value = val.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.dupX1();
t = "((" + printClass(targetClass) + ")" + t + ")";
emitSource(t + "." + fieldName
+ " = (" + printClass(field.getType()) + ")" + value + ";");
String v = HostExpr.emitUnboxArg(objx, gen, field.getType(), t + "."
+ fieldName);
gen.putField(Type.getType(targetClass), fieldName,
Type.getType(field.getType()));
ret = wrap(context, v);
} else {
String t = target.emit(C.EXPRESSION, objx, gen);
gen.push(fieldName);
String e = val.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.invokeStatic(REFLECTOR_TYPE, setInstanceFieldMethod);
ret = wrap(context, "Reflector.setInstanceField(" + t + ", \"" + fieldName + "\", " + e + ")");
}
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return ret;
}
}
}
static class StaticFieldExpr extends FieldExpr implements AssignableExpr {
// final String className;
public final String fieldName;
public final Class c;
public final java.lang.reflect.Field field;
public final Symbol tag;
// final static Method getStaticFieldMethod =
// Method.getMethod("Object getStaticField(String,String)");
// final static Method setStaticFieldMethod =
// Method.getMethod("Object setStaticField(String,String,Object)");
final int line;
final int column;
public StaticFieldExpr(int line, int column, Class c, String fieldName,
Symbol tag) {
// this.className = className;
this.fieldName = fieldName;
this.line = line;
this.column = column;
// c = Class.forName(className);
this.c = c;
try {
field = c.getField(fieldName);
} catch (NoSuchFieldException e) {
throw Util.sneakyThrow(e);
}
this.tag = tag;
}
public Object eval() {
return Reflector.getStaticField(c, fieldName);
}
public boolean canEmitPrimitive() {
return Util.isPrimitive(field.getType());
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
gen.visitLineNumber(line, gen.mark());
gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType()));
return printClass(c) + "." + fieldName;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
gen.visitLineNumber(line, gen.mark());
gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType()));
// if(context != C.STATEMENT)
String v = printClass(c) + "." + fieldName;
v = HostExpr.emitBoxReturn(objx, gen, field.getType(), v);
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
// gen.push(className);
// gen.push(fieldName);
// gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod);
return wrap(context, v);
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
// Class c = Class.forName(className);
// java.lang.reflect.Field field = c.getField(fieldName);
return tag != null ? HostExpr.tagToClass(tag) : field.getType();
}
public Object evalAssign(Expr val) {
return Reflector.setStaticField(c, fieldName, val.eval());
}
public String emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val) {
String value = val.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.dup();
emitSource(printClass(c) + "." + fieldName + " = ("
+ printClass(field.getType()) + ")" + value + ";");
String v = HostExpr.emitUnboxArg(objx, gen, field.getType(),
printClass(c) + "." + fieldName);
gen.putStatic(Type.getType(c), fieldName, Type.getType(field.getType()));
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, v);
}
}
}
static Class maybePrimitiveType(Expr e) {
if (e instanceof MaybePrimitiveExpr && e.hasJavaClass()
&& ((MaybePrimitiveExpr) e).canEmitPrimitive()) {
Class c = e.getJavaClass();
if (Util.isPrimitive(c))
return c;
}
return null;
}
static Class maybeJavaClass(Collection exprs) {
Class match = null;
try {
for (Expr e : exprs) {
if (e instanceof ThrowExpr)
continue;
if (!e.hasJavaClass())
return null;
Class c = e.getJavaClass();
if (match == null)
match = c;
else if (match != c)
return null;
}
} catch (Exception e) {
return null;
}
return match;
}
static abstract class MethodExpr extends HostExpr {
static String emitArgsAsArray(IPersistentVector args, ObjExpr objx,
GeneratorAdapter gen) {
gen.push(args.count());
gen.newArray(OBJECT_TYPE);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < args.count(); i++) {
if (sb.length() > 0) {
sb.append(", ");
}
gen.dup();
gen.push(i);
sb.append(((Expr) args.nth(i)).emit(C.EXPRESSION, objx, gen));
gen.arrayStore(OBJECT_TYPE);
}
return sb.toString();
}
public static String combineArgs(List a) {
StringBuilder sb = new StringBuilder();
for (String s : a) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(s);
}
return sb.toString();
}
public static List emitTypedArgs(ObjExpr objx,
GeneratorAdapter gen, Class[] parameterTypes, IPersistentVector args) {
ArrayList r = new ArrayList();
for (int i = 0; i < parameterTypes.length; i++) {
Expr e = (Expr) args.nth(i);
try {
StringBuilder sb = new StringBuilder();
final Class primc = maybePrimitiveType(e);
String canonicalName = printClass(parameterTypes[i]);
if (primc != null) {
sb.append("(" + canonicalName + ")");
}
if (primc == parameterTypes[i]) {
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
sb.append(pe.emitUnboxed(C.EXPRESSION, objx, gen));
} else if (primc == int.class && parameterTypes[i] == long.class) {
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
sb.append(pe.emitUnboxed(C.EXPRESSION, objx, gen));
gen.visitInsn(I2L);
} else if (primc == long.class && parameterTypes[i] == int.class) {
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
String val = pe.emitUnboxed(C.EXPRESSION, objx, gen);
if (RT.booleanCast(RT.UNCHECKED_MATH.deref())) {
gen.invokeStatic(RT_TYPE,
Method.getMethod("int uncheckedIntCast(long)"));
sb.append("RT.uncheckedIntCast(" + val + ")");
} else {
gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
sb.append("RT.intCast(" + val + ")");
}
} else if (primc == float.class && parameterTypes[i] == double.class) {
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
sb.append(pe.emitUnboxed(C.EXPRESSION, objx, gen));
gen.visitInsn(F2D);
} else if (primc == double.class && parameterTypes[i] == float.class) {
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
sb.append(pe.emitUnboxed(C.EXPRESSION, objx, gen));
gen.visitInsn(D2F);
} else {
String v = e.emit(C.EXPRESSION, objx, gen);
v = HostExpr.emitUnboxArg(objx, gen, parameterTypes[i], v);
sb.append(v);
}
r.add(sb.toString());
} catch (Exception e1) {
e1.printStackTrace(RT.errPrintWriter());
}
}
return r;
}
}
static class InstanceMethodExpr extends MethodExpr {
public final Expr target;
public final String methodName;
public final IPersistentVector args;
public final String source;
public final int line;
public final int column;
public final Symbol tag;
public final java.lang.reflect.Method method;
final static Method invokeInstanceMethodMethod = Method
.getMethod("Object invokeInstanceMethod(Object,String,Object[])");
public InstanceMethodExpr(String source, int line, int column, Symbol tag,
Expr target, String methodName, IPersistentVector args) {
this.source = source;
this.line = line;
this.column = column;
this.args = args;
this.methodName = methodName;
this.target = target;
this.tag = tag;
List methods = Reflector.getMethods(
target.hasJavaClass() && target.getJavaClass() != null ? target
.getJavaClass() : Object.class, args.count(), methodName, false);
if (methods.isEmpty())
method = null;
else {
int methodidx = 0;
if (methods.size() > 1) {
ArrayList params = new ArrayList();
ArrayList rets = new ArrayList();
for (int i = 0; i < methods.size(); i++) {
java.lang.reflect.Method m = (java.lang.reflect.Method) methods
.get(i);
params.add(m.getParameterTypes());
rets.add(m.getReturnType());
}
methodidx = getMatchingParams(methodName, params, args, rets);
}
java.lang.reflect.Method m = (java.lang.reflect.Method) (methodidx >= 0 ? methods
.get(methodidx) : null);
if (m != null) {
ObjMethod objm = (ObjMethod) METHOD.get();
if (objm != null && !objm.hasException) {
for (Class ex : m.getExceptionTypes()) {
if (!RuntimeException.class.isAssignableFrom(ex)) {
objm.hasException = true;
break;
}
}
}
}
if (m != null
&& !Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
// public method of non-public class, try to find it in hierarchy
m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m);
}
method = m;
}
if (method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) {
RT.errPrintWriter()
.format(
"Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (argument types: %s).\n",
SOURCE_PATH.deref(), line, column, methodName,
target.getJavaClass().getName(), getTypeStringForArgs(args));
}
}
public Object eval() {
try {
Object targetval = target.eval();
Object[] argvals = new Object[args.count()];
for (int i = 0; i < args.count(); i++)
argvals[i] = ((Expr) args.nth(i)).eval();
if (method != null) {
LinkedList ms = new LinkedList();
ms.add(method);
return Reflector.invokeMatchingMethod(methodName, ms, targetval,
argvals);
}
return Reflector.invokeInstanceMethod(targetval, methodName, argvals);
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException(source, line, column, e);
else
throw (CompilerException) e;
}
}
public boolean canEmitPrimitive() {
return method != null && Util.isPrimitive(method.getReturnType());
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
if (method != null) {
Type type = Type.getType(method.getDeclaringClass());
String first = target.emit(C.EXPRESSION, objx, gen);
// if(!method.getDeclaringClass().isInterface())
gen.checkCast(type);
String argsList = MethodExpr.combineArgs(MethodExpr.emitTypedArgs(objx,
gen, method.getParameterTypes(), args));
gen.visitLineNumber(line, gen.mark());
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Method m = new Method(methodName, Type.getReturnType(method),
Type.getArgumentTypes(method));
if (method.getDeclaringClass().isInterface())
gen.invokeInterface(type, m);
else
gen.invokeVirtual(type, m);
return wrap(context, "((" + printClass(method.getDeclaringClass())
+ ")" + first + ")." + method.getName() + "(" + argsList + ")");
} else
throw new UnsupportedOperationException(
"Unboxed emit of unknown member");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String ret = null;
if (method != null) {
Type type = Type.getType(method.getDeclaringClass());
String val = target.emit(C.EXPRESSION, objx, gen);
// if(!method.getDeclaringClass().isInterface())
gen.checkCast(type);
String argsList = MethodExpr.combineArgs(MethodExpr.emitTypedArgs(objx,
gen, method.getParameterTypes(), args));
gen.visitLineNumber(line, gen.mark());
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Method m = new Method(methodName, Type.getReturnType(method),
Type.getArgumentTypes(method));
if (method.getDeclaringClass().isInterface())
gen.invokeInterface(type, m);
else
gen.invokeVirtual(type, m);
// if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)
ret = "((" + printClass(method.getDeclaringClass()) + ")" + val + ")."
+ methodName + "(" + argsList + ")";
String ret1 = HostExpr.emitBoxReturn(objx, gen, method.getReturnType(),
ret);
if (context != C.STATEMENT) {
ret = ret1;
}
if (context == C.STATEMENT && ret1.equals("null")) {
ret = "";
}
} else {
String t = target.emit(C.EXPRESSION, objx, gen);
gen.push(methodName);
String argsList = emitArgsAsArray(args, objx, gen);
gen.visitLineNumber(line, gen.mark());
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
ret = "Reflector.invokeInstanceMethod(" + t + ", \"" + methodName
+ "\", new Object[]{" + argsList + "})";
}
if (context == C.STATEMENT)
gen.pop();
if (ret.isEmpty()) {
return "";
} else {
return wrap(context, ret);
}
}
public boolean hasJavaClass() {
return method != null || tag != null;
}
public Class getJavaClass() {
return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType();
}
}
static class StaticMethodExpr extends MethodExpr {
// final String className;
public final Class c;
public final String methodName;
public final IPersistentVector args;
public final String source;
public final int line;
public final int column;
public final java.lang.reflect.Method method;
public final Symbol tag;
final static Method forNameMethod = Method
.getMethod("Class classForName(String)");
final static Method invokeStaticMethodMethod = Method
.getMethod("Object invokeStaticMethod(Class,String,Object[])");
final static Keyword warnOnBoxedKeyword = Keyword.intern("warn-on-boxed");
public StaticMethodExpr(String source, int line, int column, Symbol tag,
Class c, String methodName, IPersistentVector args) {
this.c = c;
this.methodName = methodName;
this.args = args;
this.source = source;
this.line = line;
this.column = column;
this.tag = tag;
List methods = Reflector.getMethods(c, args.count(), methodName, true);
if (methods.isEmpty())
throw new IllegalArgumentException("No matching method: "
+ printClass(c) + "." + methodName);
int methodidx = 0;
if (methods.size() > 1) {
ArrayList params = new ArrayList();
ArrayList rets = new ArrayList();
for (int i = 0; i < methods.size(); i++) {
java.lang.reflect.Method m = (java.lang.reflect.Method) methods
.get(i);
params.add(m.getParameterTypes());
rets.add(m.getReturnType());
}
methodidx = getMatchingParams(methodName, params, args, rets);
}
method = (java.lang.reflect.Method) (methodidx >= 0 ? methods
.get(methodidx) : null);
if (method != null) {
ObjMethod m = (ObjMethod) METHOD.get();
if (m != null && !m.hasException) {
for (Class ex : method.getExceptionTypes()) {
if (!RuntimeException.class.isAssignableFrom(ex)) {
m.hasException = true;
break;
}
}
}
}
if (method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) {
RT.errPrintWriter()
.format(
"Reflection warning, %s:%d:%d - call to static method %s on %s can't be resolved (argument types: %s).\n",
SOURCE_PATH.deref(), line, column, methodName, c.getName(),
getTypeStringForArgs(args));
}
if (method != null
&& warnOnBoxedKeyword.equals(RT.UNCHECKED_MATH.deref())
&& isBoxedMath(method)) {
RT.errPrintWriter().format(
"Boxed math warning, %s:%d:%d - call: %s.\n", SOURCE_PATH.deref(),
line, column, method.toString());
}
}
public static boolean isBoxedMath(java.lang.reflect.Method m) {
Class c = m.getDeclaringClass();
if (c.equals(Numbers.class)) {
WarnBoxedMath boxedMath = m.getAnnotation(WarnBoxedMath.class);
if (boxedMath != null)
return boxedMath.value();
Class[] argTypes = m.getParameterTypes();
for (Class argType : argTypes)
if (argType.equals(Object.class) || argType.equals(Number.class))
return true;
}
return false;
}
public Object eval() {
try {
Object[] argvals = new Object[args.count()];
for (int i = 0; i < args.count(); i++)
argvals[i] = ((Expr) args.nth(i)).eval();
if (method != null) {
LinkedList ms = new LinkedList();
ms.add(method);
return Reflector.invokeMatchingMethod(methodName, ms, null, argvals);
}
return Reflector.invokeStaticMethod(c, methodName, argvals);
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException(source, line, column, e);
else
throw (CompilerException) e;
}
}
public boolean canEmitPrimitive() {
return method != null && Util.isPrimitive(method.getReturnType());
}
public boolean canEmitIntrinsicPredicate() {
return method != null
&& RT.get(Intrinsics.preds, method.toString()) != null;
}
public String emitIntrinsicPredicate(C context, ObjExpr objx,
GeneratorAdapter gen, Label falseLabel) {
gen.visitLineNumber(line, gen.mark());
if (method != null) {
List argsList = MethodExpr.emitTypedArgs(objx, gen,
method.getParameterTypes(), args);
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Object[] predOps = (Object[]) RT.get(Intrinsics.preds,
method.toString());
for (int i = 0; i < predOps.length - 1; i++)
gen.visitInsn((Integer) predOps[i]);
gen.visitJumpInsn((Integer) predOps[predOps.length - 1], falseLabel);
String op = (String) RT.get(SourceGenIntrinsics.preds,
method.toString());
if (argsList.size() == 1) {
return wrap(context, "(" + op + argsList.get(0) + ")");
} else if (argsList.size() == 2) {
return wrap(context, "(" + argsList.get(0) + " " + op + " "
+ argsList.get(1) + ")");
}
throw new RuntimeException("Error emiting intrinsics: " + op + " with "
+ argsList);
} else
throw new UnsupportedOperationException(
"Unboxed emit of unknown member");
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
if (method != null) {
List argsList = MethodExpr.emitTypedArgs(objx, gen,
method.getParameterTypes(), args);
gen.visitLineNumber(line, gen.mark());
// Type type = Type.getObjectType(className.replace('.', '/'));
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Object ops = RT.get(Intrinsics.ops, method.toString());
if (ops != null) {
if (ops instanceof Object[]) {
for (Object op : (Object[]) ops)
gen.visitInsn((Integer) op);
} else
gen.visitInsn((Integer) ops);
String op = (String) RT.get(SourceGenIntrinsics.ops,
method.toString());
String r;
if (op.equals("aget[]")) {
return wrap(context, argsList.get(0) + "[" + argsList.get(1) + "]");
} else if (op.equals("alength[]")) {
return wrap(context, argsList.get(0) + ".length");
} else if (argsList.size() == 1) {
return wrap(context, "(" + op + argsList.get(0) + ")");
} else if (argsList.size() == 2) {
return wrap(context, "(" + argsList.get(0) + " " + op + " "
+ argsList.get(1) + ")");
}
throw new RuntimeException("Error emiting intrinsics");
} else {
Type type = Type.getType(c);
Method m = new Method(methodName, Type.getReturnType(method),
Type.getArgumentTypes(method));
gen.invokeStatic(type, m);
return wrap(context, printClass(c) + "." + m.getName() + "("
+ MethodExpr.combineArgs(argsList) + ")");
}
} else
throw new UnsupportedOperationException(
"Unboxed emit of unknown member");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (method != null) {
String argsList = MethodExpr.combineArgs(MethodExpr.emitTypedArgs(objx,
gen, method.getParameterTypes(), args));
gen.visitLineNumber(line, gen.mark());
// Type type = Type.getObjectType(className.replace('.', '/'));
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Type type = Type.getType(c);
Method m = new Method(methodName, Type.getReturnType(method),
Type.getArgumentTypes(method));
gen.invokeStatic(type, m);
// if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)
Class retClass = method.getReturnType();
String v = printClass(c) + "." + method.getName() + "(" + argsList
+ ")";
if (context == C.STATEMENT) {
if (retClass == long.class || retClass == double.class)
gen.pop2();
else if (retClass != void.class)
gen.pop();
} else {
v = HostExpr.emitBoxReturn(objx, gen, method.getReturnType(), v);
}
return wrap(context, v);
} else {
gen.visitLineNumber(line, gen.mark());
gen.push(c.getName());
gen.invokeStatic(RT_TYPE, forNameMethod);
gen.push(methodName);
String argsList = emitArgsAsArray(args, objx, gen);
gen.visitLineNumber(line, gen.mark());
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
gen.invokeStatic(REFLECTOR_TYPE, invokeStaticMethodMethod);
if (context == C.STATEMENT)
gen.pop();
return wrap(context, "Reflector.invokeStaticMethod(" + printClass(c)
+ ".class, \"" + methodName + "\", new Object[]{" + argsList + "})");
}
}
public boolean hasJavaClass() {
return method != null || tag != null;
}
public Class getJavaClass() {
return tag != null ? HostExpr.tagToClass(tag) : method.getReturnType();
}
}
static class UnresolvedVarExpr implements Expr {
public final Symbol symbol;
public UnresolvedVarExpr(Symbol symbol) {
this.symbol = symbol;
}
public boolean hasJavaClass() {
return false;
}
public Class getJavaClass() {
throw new IllegalArgumentException("UnresolvedVarExpr has no Java class");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
throw new RuntimeException();
}
public Object eval() {
throw new IllegalArgumentException("UnresolvedVarExpr cannot be evalled");
}
}
static class NumberExpr extends LiteralExpr implements MaybePrimitiveExpr {
final Number n;
public final int id;
public NumberExpr(Number n) {
this.n = n;
this.id = registerConstant(n);
}
Object val() {
return n;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (context != C.STATEMENT) {
return wrap(context, objx.emitConstant(gen, id));
// emitUnboxed(context,objx,gen);
// HostExpr.emitBoxReturn(objx,gen,getJavaClass());
}
return "";
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
if (n instanceof Integer)
return long.class;
else if (n instanceof Double)
return double.class;
else if (n instanceof Long)
return long.class;
else
throw new IllegalStateException("Unsupported Number type: "
+ n.getClass().getName());
}
public boolean canEmitPrimitive() {
return true;
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
if (n instanceof Integer) {
gen.push(n.longValue());
return wrap(context, String.valueOf(n.longValue()));
} else if (n instanceof Double) {
gen.push(n.doubleValue());
return wrap(context, String.valueOf(n.doubleValue()));
} else if (n instanceof Long) {
gen.push(n.longValue());
return wrap(context, String.valueOf(n.longValue()) + "L");
}
throw new RuntimeException();
}
static public Expr parse(Number form) {
if (form instanceof Integer || form instanceof Double
|| form instanceof Long)
return new NumberExpr(form);
else
return new ConstantExpr(form);
}
}
static class ConstantExpr extends LiteralExpr {
// stuff quoted vals in classloader at compile time, pull out at runtime
// this won't work for static compilation...
public final Object v;
public final int id;
public ConstantExpr(Object v) {
this.v = v;
this.id = registerConstant(v);
// this.id = RT.nextID();
// DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();
// loader.registerQuotedVal(id, v);
}
Object val() {
return v;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = objx.emitConstant(gen, id);
if (context == C.STATEMENT) {
gen.pop();
// gen.loadThis();
// gen.invokeVirtual(OBJECT_TYPE, getClassMethod);
// gen.invokeVirtual(CLASS_TYPE, getClassLoaderMethod);
// gen.checkCast(DYNAMIC_CLASSLOADER_TYPE);
// gen.push(id);
// gen.invokeVirtual(DYNAMIC_CLASSLOADER_TYPE, getQuotedValMethod);
return "";
} else {
return wrap(context, val);
}
}
public boolean hasJavaClass() {
return Modifier.isPublic(v.getClass().getModifiers());
// return false;
}
public Class getJavaClass() {
if (v instanceof APersistentMap)
return APersistentMap.class;
else if (v instanceof APersistentSet)
return APersistentSet.class;
else if (v instanceof APersistentVector)
return APersistentVector.class;
else
return v.getClass();
// throw new IllegalArgumentException("Has no Java class");
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
Object v = RT.second(form);
if (v == null)
return NIL_EXPR;
else if (v == Boolean.TRUE)
return TRUE_EXPR;
else if (v == Boolean.FALSE)
return FALSE_EXPR;
if (v instanceof Number)
return NumberExpr.parse((Number) v);
else if (v instanceof String)
return new StringExpr((String) v);
else if (v instanceof IPersistentCollection
&& ((IPersistentCollection) v).count() == 0)
return new EmptyExpr(v);
else
return new ConstantExpr(v);
}
}
}
static class NilExpr extends LiteralExpr {
Object val() {
return null;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
gen.visitInsn(Opcodes.ACONST_NULL);
if (context == C.STATEMENT) {
gen.pop();
return "";
}
return wrap(context, "null");
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return null;
}
}
final static NilExpr NIL_EXPR = new NilExpr();
static class BooleanExpr extends LiteralExpr {
public final boolean val;
public BooleanExpr(boolean val) {
this.val = val;
}
Object val() {
return val ? RT.T : RT.F;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (val)
gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);
else
gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
if (context == C.STATEMENT) {
gen.pop();
return "";
}
return wrap(context, (val ? "Boolean.TRUE" : "Boolean.FALSE"));
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return Boolean.class;
}
}
final static BooleanExpr TRUE_EXPR = new BooleanExpr(true);
final static BooleanExpr FALSE_EXPR = new BooleanExpr(false);
static class StringExpr extends LiteralExpr {
public final String str;
public StringExpr(String str) {
this.str = str;
}
Object val() {
return str;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (context != C.STATEMENT) {
gen.push(str);
return wrap(context, "\"" + escapeString(str) + "\"");
} else {
return "";
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return String.class;
}
}
static class MonitorEnterExpr extends UntypedExpr {
final Expr target;
public MonitorEnterExpr(Expr target) {
this.target = target;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval monitor-enter");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
target.emit(C.EXPRESSION, objx, gen);
gen.monitorEnter();
NIL_EXPR.emit(context, objx, gen);
return "";
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
return new MonitorEnterExpr(analyze(C.EXPRESSION, RT.second(form)));
}
}
}
static class MonitorExitExpr extends UntypedExpr {
final Expr target;
public MonitorExitExpr(Expr target) {
this.target = target;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval monitor-exit");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
target.emit(C.EXPRESSION, objx, gen);
gen.monitorExit();
NIL_EXPR.emit(context, objx, gen);
return "";
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
return new MonitorExitExpr(analyze(C.EXPRESSION, RT.second(form)));
}
}
}
public static class TryExpr implements Expr {
public final Expr tryExpr;
public final Expr finallyExpr;
public final PersistentVector catchExprs;
public final int retLocal;
public final int finallyLocal;
public static class CatchClause {
// final String className;
public final Class c;
public final LocalBinding lb;
public final Expr handler;
Label label;
Label endLabel;
public CatchClause(Class c, LocalBinding lb, Expr handler) {
this.c = c;
this.lb = lb;
this.handler = handler;
}
}
public TryExpr(Expr tryExpr, PersistentVector catchExprs, Expr finallyExpr,
int retLocal, int finallyLocal) {
this.tryExpr = tryExpr;
this.catchExprs = catchExprs;
this.finallyExpr = finallyExpr;
this.retLocal = retLocal;
this.finallyLocal = finallyLocal;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval try");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
Label startTry = gen.newLabel();
Label endTry = gen.newLabel();
Label end = gen.newLabel();
Label ret = gen.newLabel();
Label finallyLabel = gen.newLabel();
for (int i = 0; i < catchExprs.count(); i++) {
CatchClause clause = (CatchClause) catchExprs.nth(i);
clause.label = gen.newLabel();
clause.endLabel = gen.newLabel();
}
String r = null;
if (context == C.EXPRESSION) {
r = registerTemp();
emitSource("Object " + r + " = null;");
}
gen.mark(startTry);
emitSource("try {");
tab();
if (catchExprs.count() > 0 && finallyExpr != null) {
emitSource("try {");
tab();
}
String e = tryExpr.emit(context, objx, gen);
if (e != null) {
emitAssigRet(context, r, e);
}
if (context != C.STATEMENT)
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), retLocal);
gen.mark(endTry);
if (catchExprs.count() == 0) {
untab();
emitSource("} finally {");
tab();
}
if (finallyExpr != null) {
if (catchExprs.count() > 0) {
untab();
emitSource("} finally {");
tab();
}
String f = finallyExpr.emit(C.STATEMENT, objx, gen);
if (f != null) {
emitAssigRet(context, r, f);
}
if (catchExprs.count() > 0) {
untab();
emitSource("}");
}
}
untab();
emitSource("}");
gen.goTo(ret);
if (catchExprs.count() > 0) {
emitSource("catch (Throwable ex___) {");
tab();
for (int i = 0; i < catchExprs.count(); i++) {
CatchClause clause = (CatchClause) catchExprs.nth(i);
gen.mark(clause.label);
// exception should be on stack
// put in clause local
String clazz = printClass(clause.c);
emitSource((i > 0 ? "else " : "") + "if (ex___ instanceof " + clazz
+ ") {");
tab();
emitSource(clazz + " " + clause.lb.print() + " = (" + clazz
+ ") ex___;");
if (finallyExpr != null) {
emitSource("try {");
tab();
}
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), clause.lb.idx);
String h = clause.handler.emit(context, objx, gen);
if (h != null) {
emitAssigRet(context, r, h);
}
if (context != C.STATEMENT)
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), retLocal);
gen.mark(clause.endLabel);
if (finallyExpr != null) {
untab();
emitSource("} finally {");
tab();
emitSource(finallyExpr.emit(C.STATEMENT, objx, gen));
untab();
emitSource("}");
}
untab();
emitSource("}");
gen.goTo(ret);
}
emitSource("else {");
tab();
emitSource("throw Util.sneakyThrow(ex___);");
untab();
emitSource("}");
untab();
emitSource("}");
}
if (finallyExpr != null) {
gen.mark(finallyLabel);
// exception should be on stack
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), finallyLocal);
Var.pushThreadBindings(RT.map(STOP_EMIT_SOURCE, true));
finallyExpr.emit(C.STATEMENT, objx, gen);
Var.popThreadBindings();
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), finallyLocal);
gen.throwException();
}
gen.mark(ret);
if (context != C.STATEMENT)
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), retLocal);
gen.mark(end);
for (int i = 0; i < catchExprs.count(); i++) {
CatchClause clause = (CatchClause) catchExprs.nth(i);
gen.visitTryCatchBlock(startTry, endTry, clause.label, clause.c
.getName().replace('.', '/'));
}
if (finallyExpr != null) {
gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);
for (int i = 0; i < catchExprs.count(); i++) {
CatchClause clause = (CatchClause) catchExprs.nth(i);
gen.visitTryCatchBlock(clause.label, clause.endLabel, finallyLabel,
null);
}
}
for (int i = 0; i < catchExprs.count(); i++) {
CatchClause clause = (CatchClause) catchExprs.nth(i);
gen.visitLocalVariable(clause.lb.name, "Ljava/lang/Object;", null,
clause.label, clause.endLabel, clause.lb.idx);
}
return (context == C.EXPRESSION ? r : "");
}
public boolean hasJavaClass() {
return tryExpr.hasJavaClass();
}
public Class getJavaClass() {
return tryExpr.getJavaClass();
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
// if(context == C.EVAL || context == C.EXPRESSION)
if (context != C.RETURN)
return analyze(context,
RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
// (try try-expr* catch-expr* finally-expr?)
// catch-expr: (catch class sym expr*)
// finally-expr: (finally expr*)
PersistentVector body = PersistentVector.EMPTY;
PersistentVector catches = PersistentVector.EMPTY;
Expr bodyExpr = null;
Expr finallyExpr = null;
boolean caught = false;
int retLocal = getAndIncLocalNum();
int finallyLocal = getAndIncLocalNum();
for (ISeq fs = form.next(); fs != null; fs = fs.next()) {
Object f = fs.first();
Object op = (f instanceof ISeq) ? ((ISeq) f).first() : null;
if (!Util.equals(op, CATCH) && !Util.equals(op, FINALLY)) {
if (caught)
throw Util
.runtimeException("Only catch or finally clause can follow catch in try expression");
body = body.cons(f);
} else {
if (bodyExpr == null)
try {
Var.pushThreadBindings(RT.map(NO_RECUR, true));
bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
} finally {
Var.popThreadBindings();
}
if (Util.equals(op, CATCH)) {
Class c = HostExpr.maybeClass(RT.second(f), false);
if (c == null)
throw new IllegalArgumentException(
"Unable to resolve classname: " + RT.second(f));
if (!(RT.third(f) instanceof Symbol))
throw new IllegalArgumentException(
"Bad binding form, expected symbol, got: " + RT.third(f));
Symbol sym = (Symbol) RT.third(f);
if (sym.getNamespace() != null)
throw Util.runtimeException("Can't bind qualified name:" + sym);
IPersistentMap dynamicBindings = RT.map(LOCAL_ENV,
LOCAL_ENV.deref(), // NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref(),
IN_CATCH_FINALLY, RT.T);
try {
Var.pushThreadBindings(dynamicBindings);
LocalBinding lb = registerLocal(sym,
(Symbol) (RT.second(f) instanceof Symbol ? RT.second(f)
: null), null, false);
Expr handler = (new BodyExpr.Parser()).parse(C.EXPRESSION,
RT.next(RT.next(RT.next(f))));
catches = catches.cons(new CatchClause(c, lb, handler));
} finally {
Var.popThreadBindings();
}
caught = true;
} else // finally
{
if (fs.next() != null)
throw Util
.runtimeException("finally clause must be last in try expression");
try {
Var.pushThreadBindings(RT.map(IN_CATCH_FINALLY, RT.T));
finallyExpr = (new BodyExpr.Parser()).parse(C.STATEMENT,
RT.next(f));
} finally {
Var.popThreadBindings();
}
}
}
}
if (bodyExpr == null) {
try {
Var.pushThreadBindings(RT.map(NO_RECUR, true));
bodyExpr = (new BodyExpr.Parser())
.parse(C.EXPRESSION, RT.seq(body));
} finally {
Var.popThreadBindings();
}
}
return new TryExpr(bodyExpr, catches, finallyExpr, retLocal,
finallyLocal);
}
}
}
// static class TryFinallyExpr implements Expr{
// final Expr tryExpr;
// final Expr finallyExpr;
//
//
// public TryFinallyExpr(Expr tryExpr, Expr finallyExpr){
// this.tryExpr = tryExpr;
// this.finallyExpr = finallyExpr;
// }
//
// public Object eval() {
// throw new UnsupportedOperationException("Can't eval try");
// }
//
// public void emit(C context, FnExpr fn, GeneratorAdapter gen){
// Label startTry = gen.newLabel();
// Label endTry = gen.newLabel();
// Label end = gen.newLabel();
// Label finallyLabel = gen.newLabel();
// gen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);
// gen.mark(startTry);
// tryExpr.emit(context, fn, gen);
// gen.mark(endTry);
// finallyExpr.emit(C.STATEMENT, fn, gen);
// gen.goTo(end);
// gen.mark(finallyLabel);
// //exception should be on stack
// finallyExpr.emit(C.STATEMENT, fn, gen);
// gen.throwException();
// gen.mark(end);
// }
//
// public boolean hasJavaClass() {
// return tryExpr.hasJavaClass();
// }
//
// public Class getJavaClass() {
// return tryExpr.getJavaClass();
// }
//
// static class Parser implements IParser{
// public Expr parse(C context, Object frm) {
// ISeq form = (ISeq) frm;
// //(try-finally try-expr finally-expr)
// if(form.count() != 3)
// throw new IllegalArgumentException(
// "Wrong number of arguments, expecting: (try-finally try-expr finally-expr) ");
//
// if(context == C.EVAL || context == C.EXPRESSION)
// return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY,
// form)));
//
// return new TryFinallyExpr(analyze(context, RT.second(form)),
// analyze(C.STATEMENT, RT.third(form)));
// }
// }
// }
static class ThrowExpr extends UntypedExpr {
public final Expr excExpr;
public ThrowExpr(Expr excExpr) {
this.excExpr = excExpr;
}
public Object eval() {
throw Util.runtimeException("Can't eval throw");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = excExpr.emit(C.EXPRESSION, objx, gen);
gen.checkCast(THROWABLE_TYPE);
gen.throwException();
emitSource("Util.trow((Throwable)" + val + ");");
return wrap(context, "null");
}
static class Parser implements IParser {
public Expr parse(C context, Object form) {
if (context == C.EVAL)
return analyze(context,
RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
return new ThrowExpr(analyze(C.EXPRESSION, RT.second(form)));
}
}
}
static public boolean subsumes(Class[] c1, Class[] c2) {
// presumes matching lengths
Boolean better = false;
for (int i = 0; i < c1.length; i++) {
if (c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class))
{
if (!c1[i].isPrimitive() && c2[i].isPrimitive()
// || Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive()
|| c2[i].isAssignableFrom(c1[i]))
better = true;
else
return false;
}
}
return better;
}
static int getMatchingParams(String methodName,
ArrayList paramlists, IPersistentVector argexprs,
List rets) {
// presumes matching lengths
int matchIdx = -1;
boolean tied = false;
boolean foundExact = false;
for (int i = 0; i < paramlists.size(); i++) {
boolean match = true;
ISeq aseq = argexprs.seq();
int exact = 0;
for (int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq
.next()) {
Expr arg = (Expr) aseq.first();
Class aclass = arg.hasJavaClass() ? arg.getJavaClass() : Object.class;
Class pclass = paramlists.get(i)[p];
if (arg.hasJavaClass() && aclass == pclass)
exact++;
else
match = Reflector.paramArgTypeMatch(pclass, aclass);
}
if (exact == argexprs.count()) {
if (!foundExact || matchIdx == -1
|| rets.get(matchIdx).isAssignableFrom(rets.get(i)))
matchIdx = i;
tied = false;
foundExact = true;
} else if (match && !foundExact) {
if (matchIdx == -1)
matchIdx = i;
else {
if (subsumes(paramlists.get(i), paramlists.get(matchIdx))) {
matchIdx = i;
tied = false;
} else if (Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) {
if (rets.get(matchIdx).isAssignableFrom(rets.get(i)))
matchIdx = i;
} else if (!(subsumes(paramlists.get(matchIdx), paramlists.get(i))))
tied = true;
}
}
}
if (tied)
throw new IllegalArgumentException(
"More than one matching method found: " + methodName);
return matchIdx;
}
public static class NewExpr implements Expr {
public final IPersistentVector args;
public final Constructor ctor;
public final Class c;
final static Method invokeConstructorMethod = Method
.getMethod("Object invokeConstructor(Class,Object[])");
final static Method forNameMethod = Method
.getMethod("Class classForName(String)");
public NewExpr(Class c, IPersistentVector args, int line, int column) {
this.args = args;
this.c = c;
Constructor[] allctors = c.getConstructors();
ArrayList ctors = new ArrayList();
ArrayList params = new ArrayList();
ArrayList rets = new ArrayList();
for (int i = 0; i < allctors.length; i++) {
Constructor ctor = allctors[i];
if (ctor.getParameterTypes().length == args.count()) {
ctors.add(ctor);
params.add(ctor.getParameterTypes());
rets.add(c);
}
}
if (ctors.isEmpty())
throw new IllegalArgumentException("No matching ctor found for " + c);
int ctoridx = 0;
if (ctors.size() > 1) {
ctoridx = getMatchingParams(c.getName(), params, args, rets);
}
this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null;
if (this.ctor != null) {
ObjMethod objm = (ObjMethod) METHOD.get();
if (objm != null && !objm.hasException) {
for (Class ex : this.ctor.getExceptionTypes()) {
if (!RuntimeException.class.isAssignableFrom(ex)) {
objm.hasException = true;
break;
}
}
}
}
if (ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) {
RT.errPrintWriter()
.format(
"Reflection warning, %s:%d:%d - call to %s ctor can't be resolved.\n",
SOURCE_PATH.deref(), line, column, c.getName());
}
}
public Object eval() {
Object[] argvals = new Object[args.count()];
for (int i = 0; i < args.count(); i++)
argvals[i] = ((Expr) args.nth(i)).eval();
if (this.ctor != null) {
try {
return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(),
argvals));
} catch (Exception e) {
throw Util.sneakyThrow(e);
}
}
return Reflector.invokeConstructor(c, argvals);
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val;
String canonicalName = printClass(c);
if (this.ctor != null) {
Type type = getType(c);
gen.newInstance(type);
gen.dup();
String argsList = MethodExpr.combineArgs(MethodExpr.emitTypedArgs(objx,
gen, ctor.getParameterTypes(), args));
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
gen.invokeConstructor(type,
new Method("", Type.getConstructorDescriptor(ctor)));
val = "new " + canonicalName + "(" + argsList + ")";
} else {
gen.push(destubClassName(c.getName()));
gen.invokeStatic(RT_TYPE, forNameMethod);
String argsList = MethodExpr.emitArgsAsArray(args, objx, gen);
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod);
val = "Reflector.invokeConstructor(" + canonicalName
+ ".class, new Object[]{" + argsList + "})";
}
if (context == C.STATEMENT)
gen.pop();
return wrap(context, val);
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return c;
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
int line = lineDeref();
int column = columnDeref();
ISeq form = (ISeq) frm;
// (new Classname args...)
if (form.count() < 2)
throw Util
.runtimeException("wrong number of arguments, expecting: (new Classname args...)");
Class c = HostExpr.maybeClass(RT.second(form), false);
if (c == null)
throw new IllegalArgumentException("Unable to resolve classname: "
+ RT.second(form));
PersistentVector args = PersistentVector.EMPTY;
for (ISeq s = RT.next(RT.next(form)); s != null; s = s.next())
args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION,
s.first()));
return new NewExpr(c, args, line, column);
}
}
}
public static class MetaExpr implements Expr {
public final Expr expr;
public final Expr meta;
final static Type IOBJ_TYPE = Type.getType(IObj.class);
final static Method withMetaMethod = Method
.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");
public MetaExpr(Expr expr, Expr meta) {
this.expr = expr;
this.meta = meta;
}
public Object eval() {
return ((IObj) expr.eval()).withMeta((IPersistentMap) meta.eval());
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = expr.emit(C.EXPRESSION, objx, gen);
gen.checkCast(IOBJ_TYPE);
String m = meta.emit(C.EXPRESSION, objx, gen);
gen.checkCast(IPERSISTENTMAP_TYPE);
gen.invokeInterface(IOBJ_TYPE, withMetaMethod);
if (context == C.STATEMENT) {
gen.pop();
return "";
}
return wrap(context, "((IObj)" + val + ").withMeta(" + m + ")");
}
public boolean hasJavaClass() {
return expr.hasJavaClass();
}
public Class getJavaClass() {
return expr.getJavaClass();
}
}
public static class IfExpr implements Expr, MaybePrimitiveExpr {
public final Expr testExpr;
public final Expr thenExpr;
public final Expr elseExpr;
public final int line;
public final int column;
public IfExpr(int line, int column, Expr testExpr, Expr thenExpr,
Expr elseExpr) {
this.testExpr = testExpr;
this.thenExpr = thenExpr;
this.elseExpr = elseExpr;
this.line = line;
this.column = column;
}
public Object eval() {
Object t = testExpr.eval();
if (t != null && t != Boolean.FALSE)
return thenExpr.eval();
return elseExpr.eval();
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
return doEmit(context, objx, gen, false);
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
return doEmit(context, objx, gen, true);
}
public String doEmit(C context, ObjExpr objx, GeneratorAdapter gen,
boolean emitUnboxed) {
if (testExpr instanceof NilExpr) {
return elseExpr.emit(context, objx, gen);
}
Label nullLabel = gen.newLabel();
Label falseLabel = gen.newLabel();
Label endLabel = gen.newLabel();
gen.visitLineNumber(line, gen.mark());
StringBuilder sb = new StringBuilder();
String ret = registerTemp();
if (C.EXPRESSION == context) {
emitSource("Object " + ret + ";");
}
sb.append("if (");
if (testExpr instanceof StaticMethodExpr
&& ((StaticMethodExpr) testExpr).canEmitIntrinsicPredicate()) {
sb.append(((StaticMethodExpr) testExpr).emitIntrinsicPredicate(
C.EXPRESSION, objx, gen, falseLabel));
} else if (maybePrimitiveType(testExpr) == boolean.class) {
sb.append(((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION,
objx, gen));
gen.ifZCmp(GeneratorAdapter.EQ, falseLabel);
} else {
String val = testExpr.emit(C.EXPRESSION, objx, gen);
gen.dup();
gen.ifNull(nullLabel);
gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
gen.visitJumpInsn(IF_ACMPEQ, falseLabel);
String temp = registerTemp();
emitSource("Object " + temp + " = " + val + ";");
sb.append(temp + " != null && " + temp + " != Boolean.FALSE");
}
sb.append(") {");
emitSource(sb.toString());
tab();
String e;
if (emitUnboxed) {
e = ((MaybePrimitiveExpr) thenExpr).emitUnboxed(context, objx, gen);
} else {
e = thenExpr.emit(context, objx, gen);
}
if (e != null) {
emitAssigRet(context, ret, e);
}
untab();
emitSource("} else {");
tab();
gen.goTo(endLabel);
gen.mark(nullLabel);
gen.pop();
gen.mark(falseLabel);
if (emitUnboxed) {
e = ((MaybePrimitiveExpr) elseExpr).emitUnboxed(context, objx, gen);
} else {
e = elseExpr.emit(context, objx, gen);
}
if (e != null) {
emitAssigRet(context, ret, e);
}
untab();
emitSource("}");
gen.mark(endLabel);
Class cast;
try {
Class thenClass = thenExpr.getJavaClass();
Class elseClass = elseExpr.getJavaClass();
cast = thenClass.isPrimitive() && thenClass == elseClass ? thenClass
: null;
} catch (Exception e2) {
cast = null;
}
return C.EXPRESSION == context ? (cast != null ? "("
+ printClass(asBoxClass(cast)) + ")" : "")
+ ret : "";
}
private Class asBoxClass(Class cast) {
switch (Type.getType(cast).getSort()) {
case Type.CHAR:
return Character.class;
case Type.BOOLEAN:
return Boolean.class;
case Type.DOUBLE:
return Double.class;
case Type.FLOAT:
return Float.class;
case Type.LONG:
return Long.class;
case Type.INT:
return Integer.class;
case Type.SHORT:
return Short.class;
case Type.BYTE:
return Byte.class;
default:
return cast;
}
}
public boolean hasJavaClass() {
return thenExpr.hasJavaClass()
&& elseExpr.hasJavaClass()
&& (thenExpr.getJavaClass() == elseExpr.getJavaClass()
|| thenExpr.getJavaClass() == RECUR_CLASS
|| elseExpr.getJavaClass() == RECUR_CLASS
|| (thenExpr.getJavaClass() == null && !elseExpr.getJavaClass()
.isPrimitive()) || (elseExpr.getJavaClass() == null && !thenExpr
.getJavaClass().isPrimitive()));
}
public boolean canEmitPrimitive() {
try {
return thenExpr instanceof MaybePrimitiveExpr
&& elseExpr instanceof MaybePrimitiveExpr
&& (thenExpr.getJavaClass() == elseExpr.getJavaClass()
|| thenExpr.getJavaClass() == RECUR_CLASS || elseExpr
.getJavaClass() == RECUR_CLASS)
&& ((MaybePrimitiveExpr) thenExpr).canEmitPrimitive()
&& ((MaybePrimitiveExpr) elseExpr).canEmitPrimitive();
} catch (Exception e) {
return false;
}
}
public Class getJavaClass() {
Class thenClass = thenExpr.getJavaClass();
if (thenClass != null && thenClass != RECUR_CLASS)
return thenClass;
return elseExpr.getJavaClass();
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
// (if test then) or (if test then else)
if (form.count() > 4)
throw Util.runtimeException("Too many arguments to if");
else if (form.count() < 3)
throw Util.runtimeException("Too few arguments to if");
PathNode branch = new PathNode(PATHTYPE.BRANCH,
(PathNode) CLEAR_PATH.get());
Expr testexpr = analyze(context == C.EVAL ? context : C.EXPRESSION,
RT.second(form));
Expr thenexpr, elseexpr;
try {
Var.pushThreadBindings(RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,
branch)));
thenexpr = analyze(context, RT.third(form));
} finally {
Var.popThreadBindings();
}
try {
Var.pushThreadBindings(RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,
branch)));
elseexpr = analyze(context, RT.fourth(form));
} finally {
Var.popThreadBindings();
}
return new IfExpr(lineDeref(), columnDeref(), testexpr, thenexpr,
elseexpr);
}
}
}
static {
// DEMUNGE_MAP maps strings to characters in the opposite
// direction that CHAR_MAP does, plus it maps DOLLAR to '/'
IPersistentMap m = RT.map(DOLLAR, '/');
for (ISeq s = RT.seq(CHAR_MAP); s != null; s = s.next()) {
IMapEntry e = (IMapEntry) s.first();
Character origCh = (Character) e.key();
String escapeStr = (String) e.val();
m = m.assoc(escapeStr, origCh);
}
DEMUNGE_MAP = m;
// DEMUNGE_PATTERN searches for the first of any occurrence of
// the strings that are keys of DEMUNGE_MAP.
// Note: Regex matching rules mean that #"_|_COLON_" "_COLON_"
// returns "_", but #"_COLON_|_" "_COLON_" returns "_COLON_"
// as desired. Sorting string keys of DEMUNGE_MAP from longest to
// shortest ensures correct matching behavior, even if some strings are
// prefixes of others.
Object[] mungeStrs = RT.toArray(RT.keys(m));
Arrays.sort(mungeStrs, new Comparator() {
public int compare(Object s1, Object s2) {
return ((String) s2).length() - ((String) s1).length();
}
});
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Object s : mungeStrs) {
String escapeStr = (String) s;
if (!first)
sb.append("|");
first = false;
sb.append("\\Q");
sb.append(escapeStr);
sb.append("\\E");
}
DEMUNGE_PATTERN = Pattern.compile(sb.toString());
}
static public String munge(String name) {
StringBuilder sb = new StringBuilder();
for (char c : name.toCharArray()) {
String sub = (String) CHAR_MAP.valAt(c);
if (sub != null)
sb.append(sub);
else
sb.append(c);
}
return sb.toString();
}
static public String demunge(String mungedName) {
StringBuilder sb = new StringBuilder();
Matcher m = DEMUNGE_PATTERN.matcher(mungedName);
int lastMatchEnd = 0;
while (m.find()) {
int start = m.start();
int end = m.end();
// Keep everything before the match
sb.append(mungedName.substring(lastMatchEnd, start));
lastMatchEnd = end;
// Replace the match with DEMUNGE_MAP result
Character origCh = (Character) DEMUNGE_MAP.valAt(m.group());
sb.append(origCh);
}
// Keep everything after the last match
sb.append(mungedName.substring(lastMatchEnd));
return sb.toString();
}
static String getTypeStringForArgs(IPersistentVector args) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < args.count(); i++) {
Expr arg = (Expr) args.nth(i);
if (i > 0)
sb.append(", ");
sb.append((arg.hasJavaClass() && arg.getJavaClass() != null) ? arg.getJavaClass().getName() : "unknown");
}
return sb.toString();
}
public static class EmptyExpr implements Expr {
public final Object coll;
final static Type HASHMAP_TYPE = Type.getType(PersistentArrayMap.class);
final static Type HASHSET_TYPE = Type.getType(PersistentHashSet.class);
final static Type VECTOR_TYPE = Type.getType(PersistentVector.class);
final static Type LIST_TYPE = Type.getType(PersistentList.class);
final static Type EMPTY_LIST_TYPE = Type
.getType(PersistentList.EmptyList.class);
public EmptyExpr(Object coll) {
this.coll = coll;
}
public Object eval() {
return coll;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String ret;
if (coll instanceof IPersistentList) {
gen.getStatic(LIST_TYPE, "EMPTY", EMPTY_LIST_TYPE);
ret = LIST_TYPE.getClassName() + ".EMPTY";
} else if (coll instanceof IPersistentVector) {
gen.getStatic(VECTOR_TYPE, "EMPTY", VECTOR_TYPE);
ret = VECTOR_TYPE.getClassName() + ".EMPTY";
} else if (coll instanceof IPersistentMap) {
gen.getStatic(HASHMAP_TYPE, "EMPTY", HASHMAP_TYPE);
ret = HASHMAP_TYPE.getClassName() + ".EMPTY";
} else if (coll instanceof IPersistentSet) {
gen.getStatic(HASHSET_TYPE, "EMPTY", HASHSET_TYPE);
ret = HASHSET_TYPE.getClassName() + ".EMPTY";
} else {
throw new UnsupportedOperationException("Unknown Collection type");
}
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, ret);
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
if (coll instanceof IPersistentList)
return IPersistentList.class;
else if (coll instanceof IPersistentVector)
return IPersistentVector.class;
else if (coll instanceof IPersistentMap)
return IPersistentMap.class;
else if (coll instanceof IPersistentSet)
return IPersistentSet.class;
else
throw new UnsupportedOperationException("Unknown Collection type");
}
}
public static class ListExpr implements Expr {
public final IPersistentVector args;
final static Method arrayToListMethod = Method
.getMethod("clojure.lang.ISeq arrayToList(Object[])");
public ListExpr(IPersistentVector args) {
this.args = args;
}
public Object eval() {
IPersistentVector ret = PersistentVector.EMPTY;
for (int i = 0; i < args.count(); i++)
ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval());
return ret.seq();
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String argsList = MethodExpr.emitArgsAsArray(args, objx, gen);
gen.invokeStatic(RT_TYPE, arrayToListMethod);
if (context == C.STATEMENT)
gen.pop();
return wrap(context, "RT.arrayToList(" + argsList + ")");
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return IPersistentList.class;
}
}
public static class MapExpr implements Expr {
public final IPersistentVector keyvals;
final static Method mapMethod = Method
.getMethod("clojure.lang.IPersistentMap map(Object[])");
final static Method mapUniqueKeysMethod = Method
.getMethod("clojure.lang.IPersistentMap mapUniqueKeys(Object[])");
public MapExpr(IPersistentVector keyvals) {
this.keyvals = keyvals;
}
public Object eval() {
Object[] ret = new Object[keyvals.count()];
for (int i = 0; i < keyvals.count(); i++)
ret[i] = ((Expr) keyvals.nth(i)).eval();
return RT.map(ret);
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
boolean allKeysConstant = true;
boolean allConstantKeysUnique = true;
IPersistentSet constantKeys = PersistentHashSet.EMPTY;
for (int i = 0; i < keyvals.count(); i += 2) {
Expr k = (Expr) keyvals.nth(i);
if (k instanceof LiteralExpr) {
Object kval = k.eval();
if (constantKeys.contains(kval))
allConstantKeysUnique = false;
else
constantKeys = (IPersistentSet) constantKeys.cons(kval);
} else
allKeysConstant = false;
}
String val = MethodExpr.emitArgsAsArray(keyvals, objx, gen);
if ((allKeysConstant && allConstantKeysUnique) || (keyvals.count() <= 2)) {
gen.invokeStatic(RT_TYPE, mapUniqueKeysMethod);
val = "RT.mapUniqueKeys(" + val + ")";
} else {
gen.invokeStatic(RT_TYPE, mapMethod);
val = "RT.map(" + val + ")";
}
if (context == C.STATEMENT) {
gen.pop();
return "";
} else {
return wrap(context, val);
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return IPersistentMap.class;
}
static public Expr parse(C context, IPersistentMap form) {
IPersistentVector keyvals = PersistentVector.EMPTY;
boolean keysConstant = true;
boolean valsConstant = true;
boolean allConstantKeysUnique = true;
IPersistentSet constantKeys = PersistentHashSet.EMPTY;
for (ISeq s = RT.seq(form); s != null; s = s.next()) {
IMapEntry e = (IMapEntry) s.first();
Expr k = analyze(context == C.EVAL ? context : C.EXPRESSION, e.key());
Expr v = analyze(context == C.EVAL ? context : C.EXPRESSION, e.val());
keyvals = (IPersistentVector) keyvals.cons(k);
keyvals = (IPersistentVector) keyvals.cons(v);
if (k instanceof LiteralExpr) {
Object kval = k.eval();
if (constantKeys.contains(kval))
allConstantKeysUnique = false;
else
constantKeys = (IPersistentSet) constantKeys.cons(kval);
} else
keysConstant = false;
if (!(v instanceof LiteralExpr))
valsConstant = false;
}
Expr ret = new MapExpr(keyvals);
if (form instanceof IObj && ((IObj) form).meta() != null)
return new MetaExpr(ret, MapExpr.parse(context == C.EVAL ? context
: C.EXPRESSION, ((IObj) form).meta()));
else if (keysConstant) {
// TBD: Add more detail to exception thrown below.
if (!allConstantKeysUnique)
throw new IllegalArgumentException("Duplicate constant keys in map");
if (valsConstant) {
IPersistentMap m = PersistentArrayMap.EMPTY;
for (int i = 0; i < keyvals.length(); i += 2) {
m = m.assoc(((LiteralExpr) keyvals.nth(i)).val(),
((LiteralExpr) keyvals.nth(i + 1)).val());
}
// System.err.println("Constant: " + m);
return new ConstantExpr(m);
} else
return ret;
} else
return ret;
}
}
public static class SetExpr implements Expr {
public final IPersistentVector keys;
final static Method setMethod = Method
.getMethod("clojure.lang.IPersistentSet set(Object[])");
public SetExpr(IPersistentVector keys) {
this.keys = keys;
}
public Object eval() {
Object[] ret = new Object[keys.count()];
for (int i = 0; i < keys.count(); i++)
ret[i] = ((Expr) keys.nth(i)).eval();
return RT.set(ret);
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String argsList = MethodExpr.emitArgsAsArray(keys, objx, gen);
gen.invokeStatic(RT_TYPE, setMethod);
if (context == C.STATEMENT)
gen.pop();
return wrap(context, "RT.set(" + argsList + ")");
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return IPersistentSet.class;
}
static public Expr parse(C context, IPersistentSet form) {
IPersistentVector keys = PersistentVector.EMPTY;
boolean constant = true;
for (ISeq s = RT.seq(form); s != null; s = s.next()) {
Object e = s.first();
Expr expr = analyze(context == C.EVAL ? context : C.EXPRESSION, e);
keys = (IPersistentVector) keys.cons(expr);
if (!(expr instanceof LiteralExpr))
constant = false;
}
Expr ret = new SetExpr(keys);
if (form instanceof IObj && ((IObj) form).meta() != null)
return new MetaExpr(ret, MapExpr.parse(context == C.EVAL ? context
: C.EXPRESSION, ((IObj) form).meta()));
else if (constant) {
IPersistentSet set = PersistentHashSet.EMPTY;
for (int i = 0; i < keys.count(); i++) {
LiteralExpr ve = (LiteralExpr) keys.nth(i);
set = (IPersistentSet) set.cons(ve.val());
}
// System.err.println("Constant: " + set);
return new ConstantExpr(set);
} else
return ret;
}
}
public static class VectorExpr implements Expr {
public final IPersistentVector args;
final static Method vectorMethod = Method
.getMethod("clojure.lang.IPersistentVector vector(Object[])");
public VectorExpr(IPersistentVector args) {
this.args = args;
}
public Object eval() {
IPersistentVector ret = PersistentVector.EMPTY;
for (int i = 0; i < args.count(); i++)
ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval());
return ret;
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = MethodExpr.emitArgsAsArray(args, objx, gen);
gen.invokeStatic(RT_TYPE, vectorMethod);
if (context == C.STATEMENT)
gen.pop();
if (val.equals("null")) {
return wrap(context, "RT.vector().cons(null)");
} else {
return wrap(context, "RT.vector(" + val + ")");
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return IPersistentVector.class;
}
static public Expr parse(C context, IPersistentVector form) {
boolean constant = true;
IPersistentVector args = PersistentVector.EMPTY;
for (int i = 0; i < form.count(); i++) {
Expr v = analyze(context == C.EVAL ? context : C.EXPRESSION,
form.nth(i));
args = (IPersistentVector) args.cons(v);
if (!(v instanceof LiteralExpr))
constant = false;
}
Expr ret = new VectorExpr(args);
if (form instanceof IObj && ((IObj) form).meta() != null)
return new MetaExpr(ret, MapExpr.parse(context == C.EVAL ? context
: C.EXPRESSION, ((IObj) form).meta()));
else if (constant) {
PersistentVector rv = PersistentVector.EMPTY;
for (int i = 0; i < args.count(); i++) {
LiteralExpr ve = (LiteralExpr) args.nth(i);
rv = rv.cons(ve.val());
}
// System.err.println("Constant: " + rv);
return new ConstantExpr(rv);
} else
return ret;
}
}
static class KeywordInvokeExpr implements Expr {
public final KeywordExpr kw;
public final Object tag;
public final Expr target;
public final int line;
public final int column;
public final int siteIndex;
public final String source;
static Type ILOOKUP_TYPE = Type.getType(ILookup.class);
public KeywordInvokeExpr(String source, int line, int column, Symbol tag,
KeywordExpr kw, Expr target) {
this.source = source;
this.kw = kw;
this.target = target;
this.line = line;
this.column = column;
this.tag = tag;
this.siteIndex = registerKeywordCallsite(kw.k);
}
public Object eval() {
try {
return kw.k.invoke(target.eval());
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException(source, line, column, e);
else
throw (CompilerException) e;
}
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
Label endLabel = gen.newLabel();
Label faultLabel = gen.newLabel();
gen.visitLineNumber(line, gen.mark());
gen.getStatic(objx.objtype, objx.thunkNameStatic(siteIndex),
ObjExpr.ILOOKUP_THUNK_TYPE);
gen.dup(); // thunk, thunk
// String val = registerTemp();
String tar = target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
// emitSource("Object " + val + " = " + tar + ";"); // thunk,thunk,target
gen.dupX2(); // target,thunk,thunk,target
gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,
Method.getMethod("Object get(Object)")); // target,thunk,result
gen.dupX2(); // result,target,thunk,result
gen.visitJumpInsn(IF_ACMPEQ, faultLabel); // result,target
gen.pop(); // result
gen.goTo(endLabel);
// emitSource("if (" + objx.thunkNameStatic(siteIndex) + " == " +
// objx.thunkNameStatic(siteIndex) + ".get(" + val + ")) {");
// tab();
gen.mark(faultLabel); // result,target
gen.swap(); // target,result
gen.pop(); // target
gen.dup(); // target,target
gen.getStatic(objx.objtype, objx.siteNameStatic(siteIndex),
ObjExpr.KEYWORD_LOOKUPSITE_TYPE); // target,target,site
gen.swap(); // target,site,target
gen.invokeInterface(ObjExpr.ILOOKUP_SITE_TYPE,
Method.getMethod("clojure.lang.ILookupThunk fault(Object)")); // target,new-thunk
gen.dup(); // target,new-thunk,new-thunk
gen.putStatic(objx.objtype, objx.thunkNameStatic(siteIndex),
ObjExpr.ILOOKUP_THUNK_TYPE); // target,new-thunk
// emitSource(objx.thunkNameStatic(siteIndex) + " = " +
// objx.siteNameStatic(siteIndex) + ".fault(" + val + ");");
gen.swap(); // new-thunk,target
gen.invokeInterface(ObjExpr.ILOOKUP_THUNK_TYPE,
Method.getMethod("Object get(Object)")); // result
// String rval = objx.thunkNameStatic(siteIndex) + ".get(" + val + ")";
// if (C.EXPRESSION != context) {
// emitSource(ret(context) + rval + statement(context));
// }
gen.mark(endLabel);
if (context == C.STATEMENT)
gen.pop();
// untab();
// emitSource("}");
// return context == C.EXPRESSION ? rval : "";
return wrap(
context,
"RT.get("
+ tar
+ ", Keyword.intern("
+ (kw.k.getNamespace() == null ? "null" : ("\""
+ kw.k.getNamespace() + "\"")) + ", \"" + kw.k.getName()
+ "\"))");
}
public boolean hasJavaClass() {
return tag != null;
}
public Class getJavaClass() {
return HostExpr.tagToClass(tag);
}
}
// static class KeywordSiteInvokeExpr implements Expr{
// public final Expr site;
// public final Object tag;
// public final Expr target;
// public final int line;
// public final int column;
// public final String source;
//
// public KeywordSiteInvokeExpr(String source, int line, int column, Symbol
// tag, Expr site, Expr target){
// this.source = source;
// this.site = site;
// this.target = target;
// this.line = line;
// this.column = column;
// this.tag = tag;
// }
//
// public Object eval() {
// try
// {
// KeywordCallSite s = (KeywordCallSite) site.eval();
// return s.thunk.invoke(s,target.eval());
// }
// catch(Throwable e)
// {
// if(!(e instanceof CompilerException))
// throw new CompilerException(source, line, column, e);
// else
// throw (CompilerException) e;
// }
// }
//
// public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
// gen.visitLineNumber(line, gen.mark());
// site.emit(C.EXPRESSION, objx, gen);
// gen.dup();
// gen.getField(Type.getType(KeywordCallSite.class),"thunk",IFN_TYPE);
// gen.swap();
// target.emit(C.EXPRESSION, objx, gen);
//
// gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE,
// ARG_TYPES[2]));
// if(context == C.STATEMENT)
// gen.pop();
// }
//
// public boolean hasJavaClass() {
// return tag != null;
// }
//
// public Class getJavaClass() {
// return HostExpr.tagToClass(tag);
// }
//
// }
public static class InstanceOfExpr implements Expr, MaybePrimitiveExpr {
Expr expr;
Class c;
public InstanceOfExpr(Class c, Expr expr) {
this.expr = expr;
this.c = c;
}
public Object eval() {
if (c.isInstance(expr.eval()))
return RT.T;
return RT.F;
}
public boolean canEmitPrimitive() {
return true;
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = expr.emit(C.EXPRESSION, objx, gen);
gen.instanceOf(getType(c));
return "(" + val + " instanceof " + printClass(c) + ")";
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = emitUnboxed(context, objx, gen);
val = HostExpr.emitBoxReturn(objx, gen, Boolean.TYPE, val);
if (context == C.STATEMENT)
gen.pop();
return wrap(context, val);
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return Boolean.TYPE;
}
}
static class StaticInvokeExpr implements Expr, MaybePrimitiveExpr {
public final Type target;
public final Class retClass;
public final Class[] paramclasses;
public final Type[] paramtypes;
public final IPersistentVector args;
public final boolean variadic;
public final Symbol tag;
StaticInvokeExpr(Type target, Class retClass, Class[] paramclasses,
Type[] paramtypes, boolean variadic, IPersistentVector args, Symbol tag) {
this.target = target;
this.retClass = retClass;
this.paramclasses = paramclasses;
this.paramtypes = paramtypes;
this.args = args;
this.variadic = variadic;
this.tag = tag;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval StaticInvokeExpr");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String val = emitUnboxed(context, objx, gen);
if (context != C.STATEMENT)
val = HostExpr.emitBoxReturn(objx, gen, retClass, val);
if (context == C.STATEMENT) {
if (retClass == long.class || retClass == double.class)
gen.pop2();
else
gen.pop();
}
return wrap(context, val);
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return tag != null ? HostExpr.tagToClass(tag) : retClass;
}
public boolean canEmitPrimitive() {
return retClass.isPrimitive();
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
Method ms = new Method("invokeStatic", getReturnType(), paramtypes);
if (variadic) {
for (int i = 0; i < paramclasses.length - 1; i++) {
Expr e = (Expr) args.nth(i);
if (maybePrimitiveType(e) == paramclasses[i]) {
// if (maybePrimitiveType(e) == paramclasses[i]) {
// ((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, objx, gen);
// } else {
// String v = e.emit(C.EXPRESSION, objx, gen);
// v = HostExpr.emitUnboxArg(objx, gen, paramclasses[i], v);
// }
// } catch (Exception ex) {
// throw Util.sneakyThrow(ex);
// }
} else {
HostExpr.emitUnboxArg(objx, gen, paramclasses[i],
e.emit(C.EXPRESSION, objx, gen));
}
}
IPersistentVector restArgs = RT.subvec(args, paramclasses.length - 1,
args.count());
MethodExpr.emitArgsAsArray(restArgs, objx, gen);
gen.invokeStatic(Type.getType(ArraySeq.class),
Method.getMethod("clojure.lang.ArraySeq create(Object[])"));
} else {
MethodExpr.emitTypedArgs(objx, gen, paramclasses, args);
}
gen.invokeStatic(target, ms);
throw new RuntimeException("NOT IMPLEMENTED; TODO FIX");
}
private Type getReturnType() {
return Type.getType(retClass);
}
public static Expr parse(Var v, ISeq args, Symbol tag) {
IPersistentCollection paramlists = (IPersistentCollection) RT.get(
v.meta(), arglistsKey);
if (paramlists == null)
throw new IllegalStateException(
"Can't call static fn with no arglists: " + v);
IPersistentVector paramlist = null;
int argcount = RT.count(args);
boolean variadic = false;
for (ISeq aseq = RT.seq(paramlists); aseq != null; aseq = aseq.next()) {
if (!(aseq.first() instanceof IPersistentVector))
throw new IllegalStateException("Expected vector arglist, had: "
+ aseq.first());
IPersistentVector alist = (IPersistentVector) aseq.first();
if (alist.count() > 1 && alist.nth(alist.count() - 2).equals(_AMP_)) {
if (argcount >= alist.count() - 2) {
paramlist = alist;
variadic = true;
}
} else if (alist.count() == argcount) {
paramlist = alist;
variadic = false;
break;
}
}
if (paramlist == null)
throw new IllegalArgumentException("Invalid arity - can't call: " + v
+ " with " + argcount + " args");
Class retClass = tagClass(tagOf(paramlist));
ArrayList paramClasses = new ArrayList();
ArrayList paramTypes = new ArrayList();
if (variadic) {
for (int i = 0; i < paramlist.count() - 2; i++) {
Class pc = tagClass(tagOf(paramlist.nth(i)));
paramClasses.add(pc);
paramTypes.add(Type.getType(pc));
}
paramClasses.add(ISeq.class);
paramTypes.add(Type.getType(ISeq.class));
} else {
for (int i = 0; i < argcount; i++) {
Class pc = tagClass(tagOf(paramlist.nth(i)));
paramClasses.add(pc);
paramTypes.add(Type.getType(pc));
}
}
String cname = v.ns.name.name.replace('.', '/').replace('-', '_')
+ DOLLAR + munge(v.sym.name);
Type target = Type.getObjectType(cname);
PersistentVector argv = PersistentVector.EMPTY;
for (ISeq s = RT.seq(args); s != null; s = s.next())
argv = argv.cons(analyze(C.EXPRESSION, s.first()));
return new StaticInvokeExpr(target, retClass,
paramClasses.toArray(new Class[paramClasses.size()]),
paramTypes.toArray(new Type[paramTypes.size()]), variadic, argv, tag);
}
}
static class InvokeExpr implements Expr {
public final Expr fexpr;
public final Object tag;
public final IPersistentVector args;
public final int line;
public final int column;
public final String source;
public boolean isProtocol = false;
public boolean isDirect = false;
public int siteIndex = -1;
public Class protocolOn;
public java.lang.reflect.Method onMethod;
static Keyword onKey = Keyword.intern("on");
static Keyword methodMapKey = Keyword.intern("method-map");
public InvokeExpr(String source, int line, int column, Symbol tag,
Expr fexpr, IPersistentVector args) {
this.source = source;
this.fexpr = fexpr;
this.args = args;
this.line = line;
this.column = column;
if (fexpr instanceof VarExpr) {
Var fvar = ((VarExpr) fexpr).var;
Var pvar = (Var) RT.get(fvar.meta(), protocolKey);
if (pvar != null && PROTOCOL_CALLSITES.isBound()) {
this.isProtocol = true;
this.siteIndex = registerProtocolCallsite(((VarExpr) fexpr).var);
Object pon = RT.get(pvar.get(), onKey);
this.protocolOn = HostExpr.maybeClass(pon, false);
if (this.protocolOn != null) {
IPersistentMap mmap = (IPersistentMap) RT.get(pvar.get(),
methodMapKey);
Keyword mmapVal = (Keyword) mmap.valAt(Keyword.intern(fvar.sym));
if (mmapVal == null) {
throw new IllegalArgumentException(
"No method of interface: "
+ protocolOn.getName()
+ " found for function: "
+ fvar.sym
+ " of protocol: "
+ pvar.sym
+ " (The protocol method may have been defined before and removed.)");
}
String mname = munge(mmapVal.sym.toString());
List methods = Reflector.getMethods(protocolOn, args.count() - 1,
mname, false);
if (methods.size() != 1)
throw new IllegalArgumentException("No single method: " + mname
+ " of interface: " + protocolOn.getName()
+ " found for function: " + fvar.sym + " of protocol: "
+ pvar.sym);
this.onMethod = (java.lang.reflect.Method) methods.get(0);
}
}
}
if (tag != null) {
this.tag = tag;
} else if (fexpr instanceof VarExpr) {
Object arglists = RT.get(RT.meta(((VarExpr) fexpr).var), arglistsKey);
Object sigTag = null;
for (ISeq s = RT.seq(arglists); s != null; s = s.next()) {
APersistentVector sig = (APersistentVector) s.first();
int restOffset = sig.indexOf(_AMP_);
if (args.count() == sig.count()
|| (restOffset > -1 && args.count() >= restOffset)) {
sigTag = tagOf(sig);
break;
}
}
this.tag = sigTag == null ? ((VarExpr) fexpr).tag : sigTag;
} else {
this.tag = null;
}
}
public Object eval() {
try {
IFn fn = (IFn) fexpr.eval();
PersistentVector argvs = PersistentVector.EMPTY;
for (int i = 0; i < args.count(); i++)
argvs = argvs.cons(((Expr) args.nth(i)).eval());
return fn.applyTo(RT.seq(Util.ret1(argvs, argvs = null)));
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException(source, line, column, e);
else
throw (CompilerException) e;
}
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
String ret;
if (isProtocol) {
gen.visitLineNumber(line, gen.mark());
ret = emitProto(context, objx, gen);
} else {
String val = fexpr.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.checkCast(IFN_TYPE);
ret = wrap(
context,
"((IFn)" + val + ").invoke("
+ emitArgsAndCall(0, context, objx, gen) + ")");
}
if (context == C.STATEMENT)
gen.pop();
return ret;
}
public String emitProto(C context, ObjExpr objx, GeneratorAdapter gen) {
Label onLabel = gen.newLabel();
Label callLabel = gen.newLabel();
Label endLabel = gen.newLabel();
Var v = ((VarExpr) fexpr).var;
Expr e = (Expr) args.nth(0);
String eetemp = registerTemp();
emitSource("Object " + eetemp + " = " + e.emit(C.EXPRESSION, objx, gen)
+ ";");
String ee = eetemp;
gen.dup(); // target, target
gen.invokeStatic(UTIL_TYPE, Method.getMethod("Class classOf(Object)")); // target,class
gen.getStatic(objx.objtype, objx.cachedClassName(siteIndex), CLASS_TYPE); // target,class,cached-class
gen.visitJumpInsn(IF_ACMPEQ, callLabel); // target
// emitSource("if (Util.classOf(" + ee + ") != this." +
// objx.cachedClassName(siteIndex) + ") {");
// tab();
if (protocolOn != null) {
gen.dup(); // target, target
gen.instanceOf(Type.getType(protocolOn));
gen.ifZCmp(GeneratorAdapter.NE, onLabel);
emitSource("if (!(" + ee + " instanceof " + printClass(protocolOn)
+ ")) {");
tab();
}
gen.dup(); // target, target
gen.invokeStatic(UTIL_TYPE, Method.getMethod("Class classOf(Object)")); // target,class
gen.putStatic(objx.objtype, objx.cachedClassName(siteIndex), CLASS_TYPE); // target
// emitSource("this." + objx.cachedClassName(siteIndex) +
// " = Util.classOf(" + ee + ");");
gen.mark(callLabel); // target
String vare = objx.emitVar(gen, v);
gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); // target,
// proto-fn
gen.swap();
String argse = emitArgsAndCall(1, context, objx, gen);
gen.goTo(endLabel);
String body = "((IFn)" + vare + ".getRawRoot()).invoke(" + ee
+ (argse.length() > 0 ? ", " + argse : "") + ")";
body = wrap(context, body);
if (context == C.EXPRESSION) {
emitAssigRet(context, eetemp, body);
} else {
emitSource(body);
}
untab();
emitSource("} else {");
tab();
gen.mark(onLabel); // target
if (protocolOn != null) {
String argList = MethodExpr
.combineArgs(MethodExpr.emitTypedArgs(objx, gen,
onMethod.getParameterTypes(), RT.subvec(args, 1, args.count())));
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod),
Type.getArgumentTypes(onMethod));
gen.invokeInterface(Type.getType(protocolOn), m);
String b = "((" + printClass(protocolOn) + ") " + ee + ")."
+ onMethod.getName() + "(" + argList + ")";
String boxed = HostExpr.emitBoxReturn(objx, gen,
onMethod.getReturnType(), b);
if (context == C.EXPRESSION) {
emitAssigRet(context, eetemp, wrap(context, boxed));
} else {
emitSource(wrap(context, b));
}
untab();
emitSource("}");
}
// untab();
// emitSource("}");
gen.mark(endLabel);
return context != C.EXPRESSION ? "" : eetemp;
}
String emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx,
GeneratorAdapter gen) {
StringBuilder sb = new StringBuilder();
for (int i = firstArgToEmit; i < Math.min(MAX_POSITIONAL_ARITY,
args.count()); i++) {
if (sb.length() > 0) {
sb.append(", ");
}
Expr e = (Expr) args.nth(i);
sb.append(e.emit(C.EXPRESSION, objx, gen));
}
if (args.count() > MAX_POSITIONAL_ARITY) {
PersistentVector restArgs = PersistentVector.EMPTY;
for (int i = MAX_POSITIONAL_ARITY; i < args.count(); i++) {
restArgs = restArgs.cons(args.nth(i));
}
sb.append(", " + MethodExpr.emitArgsAsArray(restArgs, objx, gen));
}
gen.visitLineNumber(line, gen.mark());
if (context == C.RETURN) {
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE,
ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1, args.count())]));
return sb.toString();
}
public boolean hasJavaClass() {
return tag != null;
}
public Class getJavaClass() {
return HostExpr.tagToClass(tag);
}
static public Expr parse(C context, ISeq form) {
if (context != C.EVAL)
context = C.EXPRESSION;
Expr fexpr = analyze(context, form.first());
if (fexpr instanceof VarExpr && ((VarExpr) fexpr).var.equals(INSTANCE)
&& RT.count(form) == 3) {
Expr sexpr = analyze(C.EXPRESSION, RT.second(form));
if (sexpr instanceof ConstantExpr) {
Object val = ((ConstantExpr) sexpr).val();
if (val instanceof Class) {
return new InstanceOfExpr((Class) val, analyze(context,
RT.third(form)));
}
}
}
// if(fexpr instanceof VarExpr && context != C.EVAL)
// {
// Var v = ((VarExpr)fexpr).var;
// if(RT.booleanCast(RT.get(RT.meta(v),staticKey)))
// {
// return StaticInvokeExpr.parse(v, RT.next(form), tagOf(form));
// }
// }
if (fexpr instanceof VarExpr && context != C.EVAL) {
Var v = ((VarExpr) fexpr).var;
Object arglists = RT.get(RT.meta(v), arglistsKey);
int arity = RT.count(form.next());
for (ISeq s = RT.seq(arglists); s != null; s = s.next()) {
IPersistentVector args = (IPersistentVector) s.first();
if (args.count() == arity) {
String primc = FnMethod.primInterface(args);
if (primc != null) {
return analyze(context, RT.listStar(
Symbol.intern(".invokePrim"),
((Symbol) form.first()).withMeta(RT.map(RT.TAG_KEY,
Symbol.intern(primc))), form.next()));
}
break;
}
}
}
if (fexpr instanceof KeywordExpr && RT.count(form) == 2
&& KEYWORD_CALLSITES.isBound()) {
// fexpr = new ConstantExpr(new
// KeywordCallSite(((KeywordExpr)fexpr).k));
Expr target = analyze(context, RT.second(form));
return new KeywordInvokeExpr((String) SOURCE.deref(), lineDeref(),
columnDeref(), tagOf(form), (KeywordExpr) fexpr, target);
}
PersistentVector args = PersistentVector.EMPTY;
for (ISeq s = RT.seq(form.next()); s != null; s = s.next()) {
args = args.cons(analyze(context, s.first()));
}
// if(args.count() > MAX_POSITIONAL_ARITY)
// throw new IllegalArgumentException(
// String.format("No more than %d args supported", MAX_POSITIONAL_ARITY));
return new InvokeExpr((String) SOURCE.deref(), lineDeref(),
columnDeref(), tagOf(form), fexpr, args);
}
}
static class SourceDebugExtensionAttribute extends Attribute {
public SourceDebugExtensionAttribute() {
super("SourceDebugExtension");
}
void writeSMAP(ClassWriter cw, String smap) {
ByteVector bv = write(cw, null, -1, -1, -1);
bv.putUTF8(smap);
}
}
static public class FnExpr extends ObjExpr {
final static Type aFnType = Type.getType(AFunction.class);
final static Type restFnType = Type.getType(RestFn.class);
// if there is a variadic overload (there can only be one) it is stored here
FnMethod variadicMethod = null;
IPersistentCollection methods;
// private boolean hasPrimSigs;
private boolean hasMeta;
// String superName = null;
public FnExpr(Object tag) {
super(tag);
}
public boolean hasJavaClass() {
return true;
}
boolean supportsMeta() {
return hasMeta;
}
public Class getJavaClass() {
return tag != null ? HostExpr.tagToClass(tag) : AFunction.class;
}
protected void emitMethods(ClassVisitor cv) {
// override of invoke/doInvoke for each method
for (ISeq s = RT.seq(methods); s != null; s = s.next()) {
ObjMethod method = (ObjMethod) s.first();
method.emit(this, cv);
}
if (isVariadic()) {
emitSource("public int getRequiredArity() {");
tab();
emitSource("return " + variadicMethod.reqParms.count() + ";");
untab();
emitSource("}");
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
Method.getMethod("int getRequiredArity()"), null, null, cv);
gen.visitCode();
gen.push(variadicMethod.reqParms.count());
gen.returnValue();
gen.endMethod();
}
}
static Expr parse(C context, ISeq form, String name, Object defContext) {
ISeq origForm = form;
FnExpr fn = new FnExpr(tagOf(form));
fn.src = form;
ObjMethod enclosingMethod = (ObjMethod) METHOD.deref();
if (((IMeta) form.first()).meta() != null) {
fn.onceOnly = RT.booleanCast(RT.get(RT.meta(form.first()),
Keyword.intern(null, "once")));
// fn.superName = (String) RT.get(RT.meta(form.first()),
// Keyword.intern(null, "super-name"));
}
// fn.thisName = name;
// TODO http://dev.clojure.org/jira/browse/CLJ-1330
String basename = enclosingMethod != null ? (enclosingMethod.objx.name + DOLLAR)
: // "clojure.fns." +
(munge(currentNS().name.name) + DOLLAR);
if (RT.second(form) instanceof Symbol)
name = ((Symbol) RT.second(form)).name;
String simpleName = name != null ? (munge(name).replace(".", "_DOT_") + (enclosingMethod != null ? "__"
+ nextScopedID()
: ""))
: ("fn" + "__" + nextScopedID());
/*Fix CLJ-1330
String basename = (enclosingMethod != null ?
enclosingMethod.objx.name
: (munge(currentNS().name.name))) + "$";
Symbol nm = null;
if(RT.second(form) instanceof Symbol) {
nm = (Symbol) RT.second(form);
name = nm.name + "__" + RT.nextID();
} else {
if(name == null)
name = "fn__" + RT.nextID();
else if (enclosingMethod != null)
name += "__" + RT.nextID();
}
String simpleName = munge(name).replace(".", "_DOT_");
- if(RT.second(form) instanceof Symbol)
+ if(nm != null)
{
- Symbol nm = (Symbol) RT.second(form);
*/
fn.name = basename + simpleName;
fn.internalName = fn.name.replace('.', '/');
fn.objtype = Type.getObjectType(fn.internalName);
ArrayList prims = new ArrayList();
try {
Var.pushThreadBindings(RT.mapUniqueKeys(CONSTANTS,
PersistentVector.EMPTY, CONSTANT_IDS, new IdentityHashMap(),
KEYWORDS, PersistentHashMap.EMPTY, VARS, PersistentHashMap.EMPTY,
KEYWORD_CALLSITES, PersistentVector.EMPTY, PROTOCOL_CALLSITES,
PersistentVector.EMPTY, VAR_CALLSITES, emptyVarCallSites(),
NO_RECUR, null, Compiler.NEXT_ID, new Atom(1)));
// arglist might be preceded by symbol naming this fn
if (RT.second(form) instanceof Symbol) {
Symbol nm = (Symbol) RT.second(form);
fn.thisName = nm.name;
fn.isStatic = false; // RT.booleanCast(RT.get(nm.meta(), staticKey));
form = RT.cons(FN, RT.next(RT.next(form)));
}
// now (fn [args] body...) or (fn ([args] body...) ([args2] body2...)
// ...)
// turn former into latter
if (RT.second(form) instanceof IPersistentVector)
form = RT.list(FN, RT.next(form));
fn.line = lineDeref();
fn.column = columnDeref();
FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1];
FnMethod variadicMethod = null;
for (ISeq s = RT.next(form); s != null; s = RT.next(s)) {
FnMethod f = FnMethod.parse(fn, (ISeq) RT.first(s), fn.isStatic);
if (f.isVariadic()) {
if (variadicMethod == null)
variadicMethod = f;
else
throw Util
.runtimeException("Can't have more than 1 variadic overload");
} else if (methodArray[f.reqParms.count()] == null)
methodArray[f.reqParms.count()] = f;
else
throw Util
.runtimeException("Can't have 2 overloads with same arity");
if (f.prim != null)
prims.add(f.prim);
}
if (variadicMethod != null) {
for (int i = variadicMethod.reqParms.count() + 1; i <= MAX_POSITIONAL_ARITY; i++)
if (methodArray[i] != null)
throw Util
.runtimeException("Can't have fixed arity function with more params than variadic function");
}
if (fn.isStatic && fn.closes.count() > 0)
throw new IllegalArgumentException("static fns can't be closures");
IPersistentCollection methods = null;
for (int i = 0; i < methodArray.length; i++)
if (methodArray[i] != null)
methods = RT.conj(methods, methodArray[i]);
if (variadicMethod != null)
methods = RT.conj(methods, variadicMethod);
maybeSelfContain(context, fn, defContext);
fn.methods = methods;
fn.variadicMethod = variadicMethod;
fn.keywords = (IPersistentMap) KEYWORDS.deref();
fn.vars = (IPersistentMap) VARS.deref();
fn.constants = (PersistentVector) CONSTANTS.deref();
fn.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
fn.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
fn.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
fn.constantsID = RT.nextID();
// DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();
// loader.registerConstants(fn.constantsID, fn.constants.toArray());
} finally {
Var.popThreadBindings();
}
// fn.hasPrimSigs = prims.size() > 0;
IPersistentMap fmeta = RT.meta(origForm);
if (fmeta != null)
fmeta = fmeta.without(RT.LINE_KEY).without(RT.COLUMN_KEY)
.without(RT.FILE_KEY);
fn.hasMeta = RT.count(fmeta) > 0;
try {
fn.compile(
fn.isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction",
(prims.size() == 0) ? null
: prims.toArray(new String[prims.size()]), fn.onceOnly);
} catch (IOException e) {
throw Util.sneakyThrow(e);
}
fn.getCompiledClass();
if (fn.supportsMeta()) {
// System.err.println(name + " supports meta");
return new MetaExpr(fn, MapExpr.parse(context == C.EVAL ? context
: C.EXPRESSION, fmeta));
} else
return fn;
}
private static void maybeSelfContain(C context, ObjExpr fn,
Object defContext) {
if (defContext != null) {
PersistentVector v = (PersistentVector) defContext;
fn.var = (Var) v.get(0);
IPersistentMap mm = (IPersistentMap) v.get(1);
fn.meta = mm.count() == 0 ? null : analyze(context == C.EVAL ? context
: C.EXPRESSION, mm);
fn.isDynamic = (Boolean) v.get(2);
}
}
public final ObjMethod variadicMethod() {
return variadicMethod;
}
boolean isVariadic() {
return variadicMethod != null;
}
public final IPersistentCollection methods() {
return methods;
}
public String emitForDefn(ObjExpr objx, GeneratorAdapter gen) {
// if(!hasPrimSigs && closes.count() == 0)
// {
// Type thunkType = Type.getType(FnLoaderThunk.class);
// // presumes var on stack
// gen.dup();
// gen.newInstance(thunkType);
// gen.dupX1();
// gen.swap();
// gen.push(internalName.replace('/','.'));
// gen.invokeConstructor(thunkType,Method.getMethod("void (clojure.lang.Var,String)"));
// }
// else
return emit(C.EXPRESSION, objx, gen);
}
}
static public class ObjExpr implements Expr {
static final String CONST_PREFIX = "const__";
String name;
// String simpleName;
String internalName;
String thisName;
Type objtype;
public final Object tag;
// localbinding->itself
IPersistentMap closes = PersistentHashMap.EMPTY;
// localbndingexprs
IPersistentVector closesExprs = PersistentVector.EMPTY;
// symbols
IPersistentSet volatiles = PersistentHashSet.EMPTY;
// symbol->lb
IPersistentMap fields = null;
// hinted fields
IPersistentVector hintedFields = PersistentVector.EMPTY;
// Keyword->KeywordExpr
IPersistentMap keywords = PersistentHashMap.EMPTY;
IPersistentMap vars = PersistentHashMap.EMPTY;
Class compiledClass;
int line;
int column;
PersistentVector constants;
IPersistentSet usedConstants = PersistentTreeSet.EMPTY;
int constantsID;
int altCtorDrops = 0;
IPersistentVector keywordCallsites;
IPersistentVector protocolCallsites;
IPersistentSet varCallsites;
boolean onceOnly = false;
Object src;
final static Method voidctor = Method.getMethod("void ()");
protected IPersistentMap classMeta;
protected boolean isStatic;
protected boolean isDynamic;
protected Var var;
protected Expr meta;
public final String name() {
return name;
}
// public final String simpleName(){
// return simpleName;
// }
public final String internalName() {
return internalName;
}
public final String thisName() {
return thisName;
}
public final Type objtype() {
return objtype;
}
public final IPersistentMap closes() {
return closes;
}
public final IPersistentMap keywords() {
return keywords;
}
public final IPersistentMap vars() {
return vars;
}
public final Class compiledClass() {
return compiledClass;
}
public final int line() {
return line;
}
public final int column() {
return column;
}
public final PersistentVector constants() {
return constants;
}
public final int constantsID() {
return constantsID;
}
final static Method kwintern = Method
.getMethod("clojure.lang.Keyword intern(String, String)");
final static Method symintern = Method
.getMethod("clojure.lang.Symbol intern(String)");
final static Method varintern = Method
.getMethod("clojure.lang.Var intern(clojure.lang.Symbol, clojure.lang.Symbol)");
final static Type DYNAMIC_CLASSLOADER_TYPE = Type
.getType(DynamicClassLoader.class);
final static Method getClassMethod = Method.getMethod("Class getClass()");
final static Method getClassLoaderMethod = Method
.getMethod("ClassLoader getClassLoader()");
final static Method getConstantsMethod = Method
.getMethod("Object[] getConstants(int)");
final static Method readStringMethod = Method
.getMethod("Object readString(String)");
final static Type ILOOKUP_SITE_TYPE = Type.getType(ILookupSite.class);
final static Type ILOOKUP_THUNK_TYPE = Type.getType(ILookupThunk.class);
final static Type KEYWORD_LOOKUPSITE_TYPE = Type
.getType(KeywordLookupSite.class);
private DynamicClassLoader loader;
private byte[] bytecode;
public ObjExpr(Object tag) {
this.tag = tag;
}
static String trimGenID(String name) {
int i = name.lastIndexOf("__");
return i == -1 ? name : name.substring(0, i);
}
Type[] ctorTypes() {
IPersistentVector tv = !supportsMeta() ? PersistentVector.EMPTY : RT
.vector(IPERSISTENTMAP_TYPE);
for (ISeq s = RT.keys(closes); s != null; s = s.next()) {
LocalBinding lb = (LocalBinding) s.first();
if (lb.getPrimitiveType() != null)
tv = tv.cons(Type.getType(lb.getPrimitiveType()));
else
tv = tv.cons(OBJECT_TYPE);
}
Type[] ret = new Type[tv.count()];
for (int i = 0; i < tv.count(); i++)
ret[i] = (Type) tv.nth(i);
return ret;
}
void compile(String superName, String[] interfaceNames, boolean oneTimeUse)
throws IOException {
// create bytecode for a class
// with name current_ns.defname[$letname]+
// anonymous fns get names fn__id
// derived from AFn/RestFn
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
// ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = cw;
// ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new
// PrintWriter(System.out));
// ClassVisitor cv = new TraceClassVisitor(cw, new
// PrintWriter(System.out));
Var.pushThreadBindings(RT.mapUniqueKeys(SOURCE_WRITER, cw.getSc()));
int lastSlash = internalName.lastIndexOf('/');
String className;
String packageName;
if (lastSlash != -1) {
className = internalName.substring(lastSlash + 1);
packageName = internalName.substring(0, lastSlash).replaceAll("/", ".");
} else {
packageName = "";
className = internalName;
}
emitSource("package " + packageName + ";");
emitSource();
emitSource("import clojure.lang.*;");
emitSource();
StringBuilder interfaces = new StringBuilder();
if (interfaceNames != null) {
for (String in : interfaceNames) {
if (interfaces.length() > 0) {
interfaces.append(", ");
}
interfaces.append(in.replaceAll("/", ".").replaceAll("\\$", "."));
}
}
cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, internalName, null,
superName, interfaceNames);
// superName != null ? superName :
// (isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction"),
// null);
String source = (String) SOURCE.deref();
int lineBefore = (Integer) LINE_BEFORE.deref();
int lineAfter = (Integer) LINE_AFTER.deref() + 1;
int columnBefore = (Integer) COLUMN_BEFORE.deref();
int columnAfter = (Integer) COLUMN_AFTER.deref() + 1;
if (source != null && SOURCE_PATH.deref() != null) {
// cv.visitSource(source, null);
String smap = "SMAP\n"
+ ((source.lastIndexOf('.') > 0) ? source.substring(0,
source.lastIndexOf('.')) : source)
// : simpleName)
+ ".java\n"
+ "Clojure\n"
+ "*S Clojure\n"
+ "*F\n"
+ "+ 1 "
+ source
+ "\n"
+ (String) SOURCE_PATH.deref()
+ "\n"
+ "*L\n"
+ String.format("%d#1,%d:%d\n", lineBefore, lineAfter - lineBefore,
lineBefore) + "*E";
cv.visitSource(source, smap);
}
addAnnotation(cv, classMeta);
emitSource("@com.google.j2objc.annotations.ReflectionSupport(com.google.j2objc.annotations.ReflectionSupport.Level.NATIVE_ONLY)");
emitSource("public final class " + className + " extends "
+ superName.replaceAll("/", ".")
+ (interfaces.length() > 0 ? " implements " : "") + interfaces + " {");
tab();
// static fields for constants
for (int i = 0; i < constants.count(); i++) {
emitSource("public static final " + printClass(constantType(i)) + " "
+ constantName(i) + ";");
cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, constantName(i),
constantType(i).getDescriptor(), null, null);
}
// static fields for lookup sites
for (int i = 0; i < keywordCallsites.count(); i++) {
// emitSource("public static final " +
// KEYWORD_LOOKUPSITE_TYPE.getClassName() + " " + siteNameStatic(i) +
// ";");
// emitSource("public static " + ILOOKUP_THUNK_TYPE.getClassName() + " "
// + thunkNameStatic(i) + ";");
cv.visitField(ACC_FINAL + ACC_STATIC, siteNameStatic(i),
KEYWORD_LOOKUPSITE_TYPE.getDescriptor(), null, null);
cv.visitField(ACC_STATIC, thunkNameStatic(i),
ILOOKUP_THUNK_TYPE.getDescriptor(), null, null);
}
// for(int i=0;i ()"), null,
null, cv);
clinitgen.visitCode();
clinitgen.visitLineNumber(line, clinitgen.mark());
if (var != null) {
clinitgen.push(var.ns.name.name);
clinitgen.push(var.sym.name);
clinitgen.invokeStatic(RT_TYPE,
Method.getMethod("clojure.lang.Var var(String,String)"));
clinitgen.putStatic(objtype, "VAR", VAR_TYPE);
emitSource("VAR = RT.var(\"" + var.ns.name.name + "\", \""
+ var.sym.name + "\");");
}
if (constants.count() > 0) {
emitConstants(clinitgen);
}
if (keywordCallsites.count() > 0)
emitKeywordCallsites(clinitgen);
/*
* for(int i=0;i 0) {
sb.append(", ");
}
LocalBinding lb = (LocalBinding) s.first();
if (isDeftype()) {
int access = isVolatile(lb) ? ACC_VOLATILE : isMutable(lb) ? 0
: (ACC_PUBLIC + ACC_FINAL);
FieldVisitor fv;
if (lb.getPrimitiveType() != null) {
fv = cv
.visitField(access, lb.name, Type
.getType(lb.getPrimitiveType()).getDescriptor(), null, null);
} else {
// todo - when closed-overs are fields, use more specific types here
// and in ctor and emitLocal?
fv = cv.visitField(access, lb.name, OBJECT_TYPE.getDescriptor(),
null, null);
}
String type = (lb.getPrimitiveType() != null ? printClass(lb
.getPrimitiveType()) : "Object");
sb.append("final ").append(type).append(" ").append(lb.print());
addAnnotation(fv, RT.meta(lb.sym));
emitSource("public " + (isVolatile(lb) ? "volatile" : "") + " "
+ (isMutable(lb) ? "" : "final") + " " + type + " " + lb.print()
+ ";");
} else {
// todo - only enable this non-private+writability for letfns where we
// need it
if (lb.getPrimitiveType() != null) {
cv.visitField(0 + (isVolatile(lb) ? ACC_VOLATILE : 0), lb.name,
Type.getType(lb.getPrimitiveType()).getDescriptor(), null, null);
emitSource((isVolatile(lb) ? "volatile " : "")
+ printClass(lb.getPrimitiveType()) + " " + lb.print() + ";");
sb.append("final " + printClass(lb.getPrimitiveType()) + " "
+ lb.print());
} else {
cv.visitField(0 // + (oneTimeUse ? 0 : ACC_FINAL)
, lb.name, OBJECT_TYPE.getDescriptor(), null, null);
emitSource("Object " + lb.print() + ";");
sb.append("final Object " + lb.print());
}
}
}
// static fields for callsites and thunks
for (int i = 0; i < protocolCallsites.count(); i++) {
cv.visitField(ACC_PRIVATE + ACC_STATIC, cachedClassName(i),
CLASS_TYPE.getDescriptor(), null, null);
// emitSource("private " + CLASS_TYPE.getClassName() + " " +
// cachedClassName(i) + ";");
}
// ctor that takes closed-overs and inits base + fields
Method m = new Method("", Type.VOID_TYPE, ctorTypes());
emitSource("public " + className + "(" + sb.toString() + ") {");
tab();
GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC, m, null,
null, cv);
Label start = ctorgen.newLabel();
Label end = ctorgen.newLabel();
ctorgen.visitCode();
ctorgen.visitLineNumber(line, ctorgen.mark());
ctorgen.visitLabel(start);
ctorgen.loadThis();
// if(superName != null)
ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);
emitSource("super();");
// else if(isVariadic()) //RestFn ctor takes reqArity arg
// {
// ctorgen.push(variadicMethod.reqParms.count());
// ctorgen.invokeConstructor(restFnType, restfnctor);
// }
// else
// ctorgen.invokeConstructor(aFnType, voidctor);
// if(vars.count() > 0)
// {
// ctorgen.loadThis();
// ctorgen.getStatic(VAR_TYPE,"rev",Type.INT_TYPE);
// ctorgen.push(-1);
// ctorgen.visitInsn(Opcodes.IADD);
// ctorgen.putField(objtype, "__varrev__", Type.INT_TYPE);
// }
if (supportsMeta()) {
ctorgen.loadThis();
ctorgen.visitVarInsn(IPERSISTENTMAP_TYPE.getOpcode(Opcodes.ILOAD), 1);
ctorgen.putField(objtype, "__meta", IPERSISTENTMAP_TYPE);
emitSource("this.__meta = __meta;");
}
int a = supportsMeta() ? 2 : 1;
for (ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) {
LocalBinding lb = (LocalBinding) s.first();
emitSource("this." + lb.print() + " = " + lb.print() + ";");
ctorgen.loadThis();
Class primc = lb.getPrimitiveType();
if (primc != null) {
ctorgen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), a);
ctorgen.putField(objtype, lb.name, Type.getType(primc));
if (primc == Long.TYPE || primc == Double.TYPE)
++a;
} else {
ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a);
ctorgen.putField(objtype, lb.name, OBJECT_TYPE);
}
closesExprs = closesExprs.cons(new LocalBindingExpr(lb, null));
}
ctorgen.visitLabel(end);
ctorgen.returnValue();
ctorgen.endMethod();
untab();
emitSource("}");
if (altCtorDrops > 0) {
// ctor that takes closed-overs and inits base + fields
Type[] ctorTypes = ctorTypes();
Type[] altCtorTypes = new Type[ctorTypes.length - altCtorDrops];
StringBuilder params = new StringBuilder();
StringBuilder args = new StringBuilder();
for (int i = 0; i < altCtorTypes.length; i++) {
if (params.length() > 0) {
params.append(", ");
args.append(", ");
}
params.append("o" + i);
args.append(printClass(ctorTypes[i]) + " o" + i);
altCtorTypes[i] = ctorTypes[i];
}
Method alt = new Method("", Type.VOID_TYPE, altCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC, alt, null, null, cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
for (int i = 0; i < altCtorDrops; i++) {
if (params.length() > 0) {
params.append(", ");
}
params.append("null");
ctorgen.visitInsn(Opcodes.ACONST_NULL);
}
ctorgen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE,
ctorTypes));
emitSource("public " + className + "(" + args + ") {");
tab();
emitSource("this(" + params + ");");
untab();
emitSource("}");
ctorgen.returnValue();
ctorgen.endMethod();
}
if (supportsMeta()) {
// ctor that takes closed-overs but not meta
Type[] ctorTypes = ctorTypes();
Type[] noMetaCtorTypes = new Type[ctorTypes.length - 1];
StringBuilder params = new StringBuilder();
StringBuilder paramNames = new StringBuilder();
for (int i = 1; i < ctorTypes.length; i++) {
if (params.length() > 0) {
params.append(", ");
}
paramNames.append(", ");
String t = registerTemp();
noMetaCtorTypes[i - 1] = ctorTypes[i];
params.append(printClass(ctorTypes[i]) + " " + t);
paramNames.append(t);
}
Method alt = new Method("", Type.VOID_TYPE, noMetaCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC, alt, null, null, cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.visitInsn(Opcodes.ACONST_NULL); // null meta
ctorgen.loadArgs();
ctorgen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE,
ctorTypes));
emitSource("public " + className + "(" + params + ") {");
tab();
emitSource("this(null" + paramNames + ");");
untab();
emitSource("}");
ctorgen.returnValue();
ctorgen.endMethod();
// meta()
Method meth = Method.getMethod("clojure.lang.IPersistentMap meta()");
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, meth, null,
null, cv);
gen.visitCode();
gen.loadThis();
gen.getField(objtype, "__meta", IPERSISTENTMAP_TYPE);
emitSource("public clojure.lang.IPersistentMap meta() { return this.__meta; }");
gen.returnValue();
gen.endMethod();
// withMeta()
meth = Method
.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");
emitSource("public clojure.lang.IObj withMeta(clojure.lang.IPersistentMap __meta) {");
tab();
gen = new GeneratorAdapter(ACC_PUBLIC, meth, null, null, cv);
gen.visitCode();
gen.newInstance(objtype);
gen.dup();
gen.loadArg(0);
StringBuilder ctorParams = new StringBuilder();
for (ISeq s = RT.keys(closes); s != null; s = s.next(), ++a) {
ctorParams.append(", ");
LocalBinding lb = (LocalBinding) s.first();
gen.loadThis();
Class primc = lb.getPrimitiveType();
if (primc != null) {
gen.getField(objtype, lb.name, Type.getType(primc));
ctorParams.append("(" + printClass(primc) + ")" + lb.print());
} else {
gen.getField(objtype, lb.name, OBJECT_TYPE);
ctorParams.append("(Object)" + lb.print());
}
}
emitSource("return new " + className + "(__meta" + ctorParams + ");");
gen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE,
ctorTypes));
untab();
emitSource("}");
gen.returnValue();
gen.endMethod();
}
emitStatics(cv);
emitMethods(cv);
if (keywordCallsites.count() > 0) {
Method meth = Method
.getMethod("void swapThunk(int,clojure.lang.ILookupThunk)");
// String n = registerTemp();
// String thunk = registerTemp();
// emitSource("public void swapThunk(int " + n +
// ",clojure.lang.ILookupThunk " + thunk + ") {");
// tab();
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, meth, null,
null, cv);
gen.visitCode();
Label endLabel = gen.newLabel();
Label[] labels = new Label[keywordCallsites.count()];
for (int i = 0; i < keywordCallsites.count(); i++) {
labels[i] = gen.newLabel();
}
gen.loadArg(0);
gen.visitTableSwitchInsn(0, keywordCallsites.count() - 1, endLabel,
labels);
// emitSource("switch (" + n + ") {");
for (int i = 0; i < keywordCallsites.count(); i++) {
gen.mark(labels[i]);
// gen.loadThis();
gen.loadArg(1);
gen.putStatic(objtype, thunkNameStatic(i), ILOOKUP_THUNK_TYPE);
gen.goTo(endLabel);
// emitSource("case " + i + ":");
// tab();
// emitSource(thunkNameStatic(i) + " = (" +
// ILOOKUP_THUNK_TYPE.getClassName() + ")" + thunk + ";");
// emitSource("break;");
// untab();
}
// emitSource("}");
gen.mark(endLabel);
// untab();
// emitSource("}");
gen.returnValue();
gen.endMethod();
}
// end of class
cv.visitEnd();
untab();
emitSource("}");
bytecode = cw.toByteArray();
if (RT.booleanCast(COMPILE_FILES.deref())) {
writeClassFile(internalName, bytecode);
writeSourceFile(internalName, SOURCE_WRITER.deref().toString());
}
Var.popThreadBindings();
// else
// getCompiledClass();
}
private void emitKeywordCallsites(GeneratorAdapter clinitgen) {
for (int i = 0; i < keywordCallsites.count(); i++) {
Keyword k = (Keyword) keywordCallsites.nth(i);
clinitgen.newInstance(KEYWORD_LOOKUPSITE_TYPE);
clinitgen.dup();
String val = emitValue(k, clinitgen);
clinitgen.invokeConstructor(KEYWORD_LOOKUPSITE_TYPE,
Method.getMethod("void (clojure.lang.Keyword)"));
clinitgen.dup();
clinitgen
.putStatic(objtype, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE);
clinitgen.putStatic(objtype, thunkNameStatic(i), ILOOKUP_THUNK_TYPE);
// emitSource(thunkNameStatic(i) + " = (" + siteNameStatic(i) +
// " = new " + KEYWORD_LOOKUPSITE_TYPE.getClassName() + "(" + val +
// "));");
}
}
protected void emitStatics(ClassVisitor gen) {
}
protected void emitMethods(ClassVisitor gen) {
}
String emitListAsObjectArray(Object value, GeneratorAdapter gen) {
StringBuilder sb = new StringBuilder();
gen.push(((List) value).size());
gen.newArray(OBJECT_TYPE);
int i = 0;
for (Iterator it = ((List) value).iterator(); it.hasNext(); i++) {
if (sb.length() > 0) {
sb.append(", ");
}
gen.dup();
gen.push(i);
sb.append(emitValue(it.next(), gen));
gen.arrayStore(OBJECT_TYPE);
}
return sb.toString();
}
String emitValue(Object value, GeneratorAdapter gen) {
boolean partial = true;
String str;
if (value == null) {
gen.visitInsn(Opcodes.ACONST_NULL);
str = "null";
} else if (value instanceof String) {
gen.push((String) value);
str = "\"" + escapeString((String) value) + "\"";
} else if (value instanceof Boolean) {
if (((Boolean) value).booleanValue()) {
gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);
str = "Boolean.TRUE";
} else {
gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
str = "Boolean.FALSE";
}
} else {
String valueStr = value instanceof Number ? String.valueOf(value) : "";
boolean isNeg = valueStr.startsWith("-");
if (value instanceof Integer) {
gen.push(((Integer) value).intValue());
gen.invokeStatic(Type.getType(Integer.class),
Method.getMethod("Integer valueOf(int)"));
str = isNeg ? "(" + valueStr + ")" : valueStr;
} else if (value instanceof Long) {
gen.push(((Long) value).longValue());
gen.invokeStatic(Type.getType(Long.class),
Method.getMethod("Long valueOf(long)"));
str = isNeg ? "(" + value + "L)" : value + "L";
} else if (value instanceof Double) {
gen.push(((Double) value).doubleValue());
gen.invokeStatic(Type.getType(Double.class),
Method.getMethod("Double valueOf(double)"));
str = isNeg ? "(" + valueStr + ")" : valueStr;
} else if (value instanceof Float) {
gen.push(((Float) value).floatValue());
gen.invokeStatic(Type.getType(Float.class),
Method.getMethod("Float valueOf(float)"));
str = isNeg ? "(" + valueStr + ")" : valueStr;
} else if (value instanceof Character) {
gen.push(((Character) value).charValue());
gen.invokeStatic(Type.getType(Character.class),
Method.getMethod("Character valueOf(char)"));
str = "Character.valueOf((char)"
+ (int) ((Character) value).charValue() + ")";
} else if (value instanceof Class) {
Class cc = (Class) value;
if (cc.isPrimitive()) {
Type bt;
if (cc == boolean.class)
bt = Type.getType(Boolean.class);
else if (cc == byte.class)
bt = Type.getType(Byte.class);
else if (cc == char.class)
bt = Type.getType(Character.class);
else if (cc == double.class)
bt = Type.getType(Double.class);
else if (cc == float.class)
bt = Type.getType(Float.class);
else if (cc == int.class)
bt = Type.getType(Integer.class);
else if (cc == long.class)
bt = Type.getType(Long.class);
else if (cc == short.class)
bt = Type.getType(Short.class);
else
throw Util
.runtimeException("Can't embed unknown primitive in code: "
+ value);
gen.getStatic(bt, "TYPE", Type.getType(Class.class));
} else {
gen.push(destubClassName(cc.getName()));
gen.invokeStatic(RT_TYPE, Method.getMethod("Class classForName(String)"));
}
str = printClass(cc) + ".class";
} else if (value instanceof Symbol) {
String ns_ = ((Symbol) value).ns;
gen.push(ns_);
String name_ = ((Symbol) value).name;
gen.push(name_);
gen.invokeStatic(Type.getType(Symbol.class),
Method.getMethod("clojure.lang.Symbol intern(String,String)"));
str = "Symbol.intern(" + (ns_ == null ? "null" : "\"" + ns_ + "\"")
+ ", " + (name_ == null ? "null" : "\"" + name_ + "\"") + ")";
} else if (value instanceof Keyword) {
String ns_ = ((Keyword) value).sym.ns;
gen.push(ns_);
String name_ = ((Keyword) value).sym.name;
gen.push(name_);
gen.invokeStatic(RT_TYPE,
Method.getMethod("clojure.lang.Keyword keyword(String,String)"));
str = "Keyword.intern(" + (ns_ == null ? "null" : "\"" + ns_ + "\"")
+ ", " + (name_ == null ? "null" : "\"" + name_ + "\"") + ")";
}
// else if(value instanceof KeywordCallSite)
// {
// emitValue(((KeywordCallSite) value).k.sym, gen);
// gen.invokeStatic(Type.getType(KeywordCallSite.class),
// Method.getMethod("clojure.lang.KeywordCallSite create(clojure.lang.Symbol)"));
// }
else if (value instanceof Var) {
Var var = (Var) value;
String className = maybeClassWithVAR(var);
if (className != null) {
gen.getStatic(
Type.getType("L" + className.replaceAll("\\.", "/") + ";"),
"VAR", VAR_TYPE);
return className + ".VAR";
} else {
String ns_ = var.ns.name.toString();
String name_ = var.sym.toString();
gen.push(ns_);
gen.push(name_);
gen.invokeStatic(RT_TYPE,
Method.getMethod("clojure.lang.Var var(String,String)"));
str = "RT.var(" + (ns_ == null ? "null" : "\"" + ns_ + "\"") + ", "
+ (name_ == null ? "null" : "\"" + name_ + "\"") + ")";
}
} else if (value instanceof IType) {
Method ctor = new Method(
"",
Type.getConstructorDescriptor(value.getClass().getConstructors()[0]));
gen.newInstance(Type.getType(value.getClass()));
gen.dup();
IPersistentVector fields = (IPersistentVector) Reflector
.invokeStaticMethod(value.getClass(), "getBasis", new Object[] {});
StringBuilder sb = new StringBuilder();
for (ISeq s = RT.seq(fields); s != null; s = s.next()) {
Symbol field = (Symbol) s.first();
Class k = tagClass(tagOf(field));
Object val = Reflector.getInstanceField(value, field.name);
String f = emitValue(val, gen);
if (k.isPrimitive()) {
Type b = Type.getType(boxClass(k));
String p = Type.getType(k).getDescriptor();
String n = k.getName();
gen.invokeVirtual(b, new Method(n + "Value", "()" + p));
f = f + "." + n + "Value()";
}
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(f);
}
gen.invokeConstructor(Type.getType(value.getClass()), ctor);
return "new " + printClass(value.getClass()) + "(" + sb.toString()
+ ")";
} else if (value instanceof IRecord) {
Method createMethod = Method.getMethod(value.getClass().getName()
+ " create(clojure.lang.IPersistentMap)");
String val = emitValue(
PersistentArrayMap.create((java.util.Map) value), gen);
gen.invokeStatic(getType(value.getClass()), createMethod);
str = printClass(value.getClass()) + ".create(" + val + ")";
} else if (value instanceof IPersistentMap) {
List entries = new ArrayList();
for (Map.Entry entry : (Set) ((Map) value).entrySet()) {
entries.add(entry.getKey());
entries.add(entry.getValue());
}
String val = emitListAsObjectArray(entries, gen);
gen.invokeStatic(RT_TYPE,
Method.getMethod("clojure.lang.IPersistentMap map(Object[])"));
str = "RT.map(" + val + ")";
} else if (value instanceof IPersistentVector) {
String val = emitListAsObjectArray(value, gen);
gen.invokeStatic(RT_TYPE, Method
.getMethod("clojure.lang.IPersistentVector vector(Object[])"));
if (val.equals("null")) {
str = "RT.vector().cons(null)";
} else {
str = "RT.vector(" + val + ")";
}
} else if (value instanceof PersistentHashSet) {
ISeq vs = RT.seq(value);
if (vs == null) {
gen.getStatic(Type.getType(PersistentHashSet.class), "EMPTY",
Type.getType(PersistentHashSet.class));
str = "PersistentHashSet.EMPTY";
} else {
String val = emitListAsObjectArray(vs, gen);
gen.invokeStatic(Type.getType(PersistentHashSet.class), Method
.getMethod("clojure.lang.PersistentHashSet create(Object[])"));
if (val.equals("null")) {
str = "PersistentHashSet.create().cons(null)";
} else {
str = "PersistentHashSet.create(" + val + ")";
}
}
} else if (value instanceof ISeq || value instanceof IPersistentList) {
String val = emitListAsObjectArray(value, gen);
gen.invokeStatic(Type.getType(java.util.Arrays.class),
Method.getMethod("java.util.List asList(Object[])"));
gen.invokeStatic(Type.getType(PersistentList.class), Method
.getMethod("clojure.lang.IPersistentList create(java.util.List)"));
if (val.equals("null")) {
str = "PersistentList.EMPTY.cons(null)";
} else {
str = "PersistentList.create(java.util.Arrays.asList(" + val + "))";
}
} else if (value instanceof Pattern) {
String v = emitValue(value.toString(), gen);
gen.invokeStatic(Type.getType(Pattern.class),
Method.getMethod("java.util.regex.Pattern compile(String)"));
str = "java.util.regex.Pattern.compile(" + v + ")";
} else {
String cs = null;
try {
cs = RT.printString(value);
// System.out.println("WARNING SLOW CODE: " + Util.classOf(value) +
// " -> " + cs);
} catch (Exception e) {
throw Util
.runtimeException("Can't embed object in code, maybe print-dup not defined: "
+ value);
}
if (cs.length() == 0)
throw Util
.runtimeException("Can't embed unreadable object in code: "
+ value);
if (cs.startsWith("#<"))
throw Util
.runtimeException("Can't embed unreadable object in code: "
+ cs);
gen.push(cs);
gen.invokeStatic(RT_TYPE, readStringMethod);
partial = false;
str = "RT.readString(\"" + escapeString(cs) + "\")";
}
}
if (partial) {
if (value instanceof IObj && RT.count(((IObj) value).meta()) > 0) {
gen.checkCast(IOBJ_TYPE);
Object m = ((IObj) value).meta();
String val = emitValue(elideMeta(m), gen);
gen.checkCast(IPERSISTENTMAP_TYPE);
gen.invokeInterface(
IOBJ_TYPE,
Method
.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)"));
return "((clojure.lang.IObj)" + str + ").withMeta((IPersistentMap)"
+ val + ")";
}
}
return str;
}
private String maybeClassWithVAR(Var var) {
try {
Class c = Class.forName(
var.ns.name.toString() + DOLLAR + munge(var.sym.toString()), false,
getClass().getClassLoader());
c.getDeclaredField("VAR");
return c.getCanonicalName();
} catch (Throwable e) {
return null;
}
}
void emitConstants(GeneratorAdapter clinitgen) {
try {
Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));
for (int i = 0; i < constants.count(); i++) {
String val = emitValue(constants.nth(i), clinitgen);
clinitgen.checkCast(constantType(i));
clinitgen.putStatic(objtype, constantName(i), constantType(i));
emitSource(constantName(i) + " = (" + printClass(constantType(i))
+ ")" + val + ";");
}
} finally {
Var.popThreadBindings();
}
}
boolean isMutable(LocalBinding lb) {
return isVolatile(lb)
|| RT.booleanCast(RT.contains(fields, lb.sym))
&& RT.booleanCast(RT.get(lb.sym.meta(),
Keyword.intern("unsynchronized-mutable")));
}
boolean isVolatile(LocalBinding lb) {
return RT.booleanCast(RT.contains(fields, lb.sym))
&& RT.booleanCast(RT.get(lb.sym.meta(),
Keyword.intern("volatile-mutable")));
}
boolean isDeftype() {
return fields != null;
}
boolean supportsMeta() {
return !isDeftype();
}
void emitClearCloses(GeneratorAdapter gen) {
// int a = 1;
// for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)
// {
// LocalBinding lb = (LocalBinding) s.first();
// Class primc = lb.getPrimitiveType();
// if(primc == null)
// {
// gen.loadThis();
// gen.visitInsn(Opcodes.ACONST_NULL);
// gen.putField(objtype, lb.name, OBJECT_TYPE);
// }
// }
}
synchronized Class getCompiledClass() {
if (compiledClass == null) {
// if(RT.booleanCast(COMPILE_FILES.deref()))
// compiledClass = RT.classForName(name);//loader.defineClass(name,
// bytecode);
// else
loader = (DynamicClassLoader) LOADER.deref();
compiledClass = loader.defineClass(name, bytecode, src);
}
return compiledClass;
}
public Object eval() {
if (isDeftype())
return null;
try {
return getCompiledClass().newInstance();
} catch (Exception e) {
throw Util.sneakyThrow(e);
}
}
public void emitLetFnInits(GeneratorAdapter gen, ObjExpr objx,
IPersistentSet letFnLocals) {
// objx arg is enclosing objx, not this
gen.checkCast(objtype);
for (ISeq s = RT.keys(closes); s != null; s = s.next()) {
LocalBinding lb = (LocalBinding) s.first();
if (letFnLocals.contains(lb)) {
Class primc = lb.getPrimitiveType();
gen.dup();
if (primc != null) {
objx.emitUnboxedLocal(gen, lb);
gen.putField(objtype, lb.name, Type.getType(primc));
} else {
objx.emitLocal(gen, lb, false);
gen.putField(objtype, lb.name, OBJECT_TYPE);
}
}
}
gen.pop();
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
// emitting a Fn means constructing an instance, feeding closed-overs from
// enclosing scope, if any
// objx arg is enclosing objx, not this
// getCompiledClass();
String val;
if (isDeftype()) {
gen.visitInsn(Opcodes.ACONST_NULL);
val = "";
} else {
gen.newInstance(objtype);
gen.dup();
if (supportsMeta())
gen.visitInsn(Opcodes.ACONST_NULL);
StringBuilder argsList = new StringBuilder();
for (ISeq s = RT.seq(closesExprs); s != null; s = s.next()) {
if (argsList.length() > 0) {
argsList.append(", ");
}
LocalBindingExpr lbe = (LocalBindingExpr) s.first();
LocalBinding lb = lbe.b;
if (lb.getPrimitiveType() != null)
argsList.append(objx.emitUnboxedLocal(gen, lb));
else
argsList.append(objx.emitLocal(gen, lb, lbe.shouldClear));
}
gen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE,
ctorTypes()));
val = "new " + printClass(objtype) + "(" + argsList + ")";
}
if (context == C.STATEMENT) {
gen.pop();
}
if (!val.isEmpty()) {
return wrap(context, val);
} else {
return "";
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return (compiledClass != null) ? compiledClass : (tag != null) ? HostExpr
.tagToClass(tag) : IFn.class;
}
public void emitAssignLocal(GeneratorAdapter gen, LocalBinding lb, Expr val) {
if (!isMutable(lb))
throw new IllegalArgumentException("Cannot assign to non-mutable: "
+ lb.name);
Class primc = lb.getPrimitiveType();
gen.loadThis();
if (primc != null) {
if (!(val instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr) val)
.canEmitPrimitive()))
throw new IllegalArgumentException(
"Must assign primitive to primitive mutable: " + lb.name);
MaybePrimitiveExpr me = (MaybePrimitiveExpr) val;
String t = me.emitUnboxed(C.EXPRESSION, this, gen);
gen.putField(objtype, lb.name, Type.getType(primc));
emitSource("this." + lb.print() + " = " + t + ";");
} else {
String t = val.emit(C.EXPRESSION, this, gen);
gen.putField(objtype, lb.name, OBJECT_TYPE);
emitSource("this." + lb.print() + " = " + t + ";");
}
}
private String emitLocal(GeneratorAdapter gen, LocalBinding lb,
boolean clear) {
if (closes.containsKey(lb)) {
Class primc = lb.getPrimitiveType();
gen.loadThis();
if (primc != null) {
gen.getField(objtype, lb.name, Type.getType(primc));
return HostExpr.emitBoxReturn(this, gen, primc, "this." + lb.print());
} else {
gen.getField(objtype, lb.name, OBJECT_TYPE);
if (onceOnly && clear && lb.canBeCleared) {
gen.loadThis();
gen.visitInsn(Opcodes.ACONST_NULL);
gen.putField(objtype, lb.name, OBJECT_TYPE);
}
return "this." + lb.print();
}
} else {
int argoff = isStatic ? 0 : 1;
Class primc = lb.getPrimitiveType();
// String rep = lb.sym.name + " " +
// lb.toString().substring(lb.toString().lastIndexOf('@'));
if (lb.isArg) {
gen.loadArg(lb.idx - argoff);
if (primc != null)
return HostExpr.emitBoxReturn(this, gen, primc, lb.print());
else {
if (clear && lb.canBeCleared) {
// System.out.println("clear: " + rep);
gen.visitInsn(Opcodes.ACONST_NULL);
gen.storeArg(lb.idx - argoff);
} else {
// System.out.println("use: " + rep);
}
return lb.print();
}
} else {
if (primc != null) {
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD),
lb.idx);
return HostExpr.emitBoxReturn(this, gen, primc, lb.print());
} else {
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx);
if (clear && lb.canBeCleared) {
// System.out.println("clear: " + rep);
gen.visitInsn(Opcodes.ACONST_NULL);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);
} else {
// System.out.println("use: " + rep);
}
if (METHOD.deref() instanceof FnMethod) {
FnMethod m = (FnMethod) METHOD.deref();
if (m.thisName != null && lb.name.equals(munge(m.thisName))) {
return "this";
}
}
if (METHOD.deref() instanceof NewInstanceMethod) {
NewInstanceMethod m = (NewInstanceMethod) METHOD.deref();
Symbol tname = m.thisName;
if (tname.name != null && lb.name.equals(munge(tname.name))) {
return "this";
}
}
if (thisName != null && lb.name.equals(munge(thisName))) {
return "this";
} else {
return lb.print();
}
}
}
}
}
private String emitUnboxedLocal(GeneratorAdapter gen, LocalBinding lb) {
int argoff = isStatic ? 0 : 1;
Class primc = lb.getPrimitiveType();
if (closes.containsKey(lb)) {
gen.loadThis();
gen.getField(objtype, lb.name, Type.getType(primc));
} else if (lb.isArg) {
gen.loadArg(lb.idx - argoff);
} else {
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);
}
return lb.print();
}
public String emitVar(GeneratorAdapter gen, Var var) {
Integer i = (Integer) vars.valAt(var);
return emitConstant(gen, i);
// gen.getStatic(fntype, munge(var.sym.toString()), VAR_TYPE);
}
final static Method varGetMethod = Method.getMethod("Object get()");
final static Method varGetRawMethod = Method
.getMethod("Object getRawRoot()");
public String emitVarValue(GeneratorAdapter gen, Var v) {
Integer i = (Integer) vars.valAt(v);
if (!v.isDynamic()) {
String val = emitConstant(gen, i);
gen.invokeVirtual(VAR_TYPE, varGetRawMethod);
return val + ".getRawRoot()";
} else {
String val = emitConstant(gen, i);
gen.invokeVirtual(VAR_TYPE, varGetMethod);
return val + ".get()";
}
}
public String emitKeyword(GeneratorAdapter gen, Keyword k) {
Integer i = (Integer) keywords.valAt(k);
return emitConstant(gen, i);
// gen.getStatic(fntype, munge(k.sym.toString()), KEYWORD_TYPE);
}
public String emitConstant(GeneratorAdapter gen, int id) {
usedConstants = (IPersistentSet) usedConstants.cons(id);
String constantName = constantName(id);
Type constantType = constantType(id);
gen.getStatic(objtype, constantName, constantType);
return constantName;
}
String constantName(int id) {
return CONST_PREFIX + id;
}
String siteName(int n) {
return "__site__" + n;
}
String siteNameStatic(int n) {
return siteName(n) + "__";
}
String thunkName(int n) {
return "__thunk__" + n;
}
String cachedClassName(int n) {
return "__cached_class__" + n;
}
String cachedVarName(int n) {
return "__cached_var__" + n;
}
String varCallsiteName(int n) {
return "__var__callsite__" + n;
}
String thunkNameStatic(int n) {
return thunkName(n) + "__";
}
Class constantRealType(int id) {
Object o = constants.nth(id);
Class c = clojure.lang.Util.classOf(o);
if (o != null) {
if (o instanceof ISeq || o instanceof IPersistentList) {
return IPersistentList.class;
} else if (o instanceof IPersistentMap) {
return IPersistentMap.class;
}
}
return c;
}
Type constantType(int id) {
Object o = constants.nth(id);
Class c = clojure.lang.Util.classOf(o);
if (c != null && Modifier.isPublic(c.getModifiers())) {
// can't emit derived fn types due to visibility
if (LazySeq.class.isAssignableFrom(c))
return Type.getType(ISeq.class);
else if (c == Keyword.class)
return Type.getType(Keyword.class);
// else if(c == KeywordCallSite.class)
// return Type.getType(KeywordCallSite.class);
else if (RestFn.class.isAssignableFrom(c))
return Type.getType(RestFn.class);
else if (IPersistentMap.class.isAssignableFrom(c))
return Type.getType(IPersistentMap.class);
else if (IPersistentSet.class.isAssignableFrom(c))
return Type.getType(IPersistentSet.class);
else if (IPersistentStack.class.isAssignableFrom(c))
return Type.getType(IPersistentStack.class);
else if (IPersistentVector.class.isAssignableFrom(c))
return Type.getType(IPersistentVector.class);
else if (IPersistentList.class.isAssignableFrom(c))
return Type.getType(IPersistentList.class);
else if (Symbol.class.isAssignableFrom(c))
return Type.getType(Symbol.class);
else if (AFn.class.isAssignableFrom(c))
return Type.getType(AFn.class);
else if (c == Var.class)
return Type.getType(Var.class);
else if (c == String.class)
return Type.getType(String.class);
// return Type.getType(c);
}
return OBJECT_TYPE;
}
}
enum PATHTYPE {
PATH, BRANCH;
}
static class PathNode {
final PATHTYPE type;
final PathNode parent;
PathNode(PATHTYPE type, PathNode parent) {
this.type = type;
this.parent = parent;
}
}
static PathNode clearPathRoot() {
return (PathNode) CLEAR_ROOT.get();
}
public static String escapeString(String value) {
return StringEscapeUtils.escapeJava(value);
}
enum PSTATE {
REQ, REST, DONE
}
public static class FnMethod extends ObjMethod {
// localbinding->localbinding
PersistentVector reqParms = PersistentVector.EMPTY;
LocalBinding restParm = null;
Type[] argtypes;
Class[] argclasses;
Class retClass;
String prim;
private String thisName;
public FnMethod(ObjExpr objx, ObjMethod parent) {
super(objx, parent);
}
static public char classChar(Object x) {
Class c = null;
if (x instanceof Class)
c = (Class) x;
else if (x instanceof Symbol)
c = primClass((Symbol) x);
if (c == null || !c.isPrimitive())
return 'O';
if (c == long.class)
return 'L';
if (c == double.class)
return 'D';
throw new IllegalArgumentException(
"Only long and double primitives are supported");
}
static public String primInterface(IPersistentVector arglist) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arglist.count(); i++)
sb.append(classChar(tagOf(arglist.nth(i))));
sb.append(classChar(tagOf(arglist)));
String ret = sb.toString();
boolean prim = ret.contains("L") || ret.contains("D");
if (prim && arglist.count() > 4)
throw new IllegalArgumentException(
"fns taking primitives support only 4 or fewer args");
if (prim)
return "clojure.lang.IFn$" + ret;
return null;
}
static FnMethod parse(ObjExpr objx, ISeq form, boolean isStatic) {
// ([args] body...)
IPersistentVector parms = (IPersistentVector) RT.first(form);
ISeq body = RT.next(form);
try {
FnMethod method = new FnMethod(objx, (ObjMethod) METHOD.deref());
method.line = lineDeref();
method.column = columnDeref();
// register as the current method and set up a new env frame
PathNode pnode = (PathNode) CLEAR_PATH.get();
if (pnode == null)
pnode = new PathNode(PATHTYPE.PATH, null);
Var.pushThreadBindings(RT.mapUniqueKeys(METHOD, method, LOCAL_ENV,
LOCAL_ENV.deref(), LOOP_LOCALS, null, NEXT_LOCAL_NUM, 0,
CLEAR_PATH, pnode, CLEAR_ROOT, pnode, CLEAR_SITES,
PersistentHashMap.EMPTY));
method.prim = primInterface(parms);
if (method.prim != null)
method.prim = method.prim.replace('.', '/');
method.retClass = tagClass(tagOf(parms));
if (method.retClass.isPrimitive()
&& !(method.retClass == double.class || method.retClass == long.class))
throw new IllegalArgumentException(
"Only long and double primitives are supported");
// register 'this' as local 0
// registerLocal(THISFN, null, null);
if (!isStatic) {
method.thisName = objx.thisName;
if (objx.thisName != null) {
registerLocal(Symbol.intern(objx.thisName), null, null, false);
} else {
getAndIncLocalNum();
}
}
PSTATE state = PSTATE.REQ;
PersistentVector argLocals = PersistentVector.EMPTY;
ArrayList argtypes = new ArrayList();
ArrayList argclasses = new ArrayList();
for (int i = 0; i < parms.count(); i++) {
if (!(parms.nth(i) instanceof Symbol))
throw new IllegalArgumentException("fn params must be Symbols: "
+ parms.nth(i));
Symbol p = (Symbol) parms.nth(i);
if (p.getNamespace() != null)
throw Util
.runtimeException("Can't use qualified name as parameter: " + p);
if (p.equals(_AMP_)) {
// if(isStatic)
// throw Util.runtimeException("Variadic fns cannot be static");
if (state == PSTATE.REQ)
state = PSTATE.REST;
else
throw Util.runtimeException("Invalid parameter list");
}
else {
Class pc = primClass(tagClass(tagOf(p)));
// if(pc.isPrimitive() && !isStatic)
// {
// pc = Object.class;
// p = (Symbol) ((IObj) p).withMeta((IPersistentMap)
// RT.assoc(RT.meta(p), RT.TAG_KEY, null));
// }
// throw
// Util.runtimeException("Non-static fn can't have primitive parameter: "
// + p);
if (pc.isPrimitive() && !(pc == double.class || pc == long.class))
throw new IllegalArgumentException(
"Only long and double primitives are supported: " + p);
if (state == PSTATE.REST && tagOf(p) != null)
throw Util.runtimeException("& arg cannot have type hint");
if (state == PSTATE.REST && method.prim != null)
throw Util
.runtimeException("fns taking primitives cannot be variadic");
if (state == PSTATE.REST)
pc = ISeq.class;
argtypes.add(Type.getType(pc));
argclasses.add(pc);
LocalBinding lb = pc.isPrimitive() ? registerLocal(p, null,
new MethodParamExpr(pc), true) : registerLocal(p,
state == PSTATE.REST ? ISEQ : tagOf(p), null, true);
argLocals = argLocals.cons(lb);
switch (state) {
case REQ:
method.reqParms = method.reqParms.cons(lb);
break;
case REST:
method.restParm = lb;
state = PSTATE.DONE;
break;
default:
throw Util.runtimeException("Unexpected parameter");
}
}
}
if (method.reqParms.count() > MAX_POSITIONAL_ARITY)
throw Util.runtimeException("Can't specify more than "
+ MAX_POSITIONAL_ARITY + " params");
LOOP_LOCALS.set(argLocals);
method.argLocals = argLocals;
// if(isStatic)
if (method.prim != null) {
method.argtypes = argtypes.toArray(new Type[argtypes.size()]);
method.argclasses = argclasses.toArray(new Class[argtypes.size()]);
for (int i = 0; i < method.argclasses.length; i++) {
if (method.argclasses[i] == long.class
|| method.argclasses[i] == double.class)
getAndIncLocalNum();
}
}
method.body = (new BodyExpr.Parser()).parse(C.RETURN, body);
return method;
} finally {
Var.popThreadBindings();
}
}
public void emit(ObjExpr fn, ClassVisitor cv) {
if (prim != null)
doEmitPrim(fn, cv);
else if (fn.isStatic)
doEmitStatic(fn, cv);
else
doEmit(fn, cv);
}
// TODO: is this required?
public void doEmitStatic(ObjExpr fn, ClassVisitor cv) {
new RuntimeException().printStackTrace();
Method ms = new Method("invokeStatic", getReturnType(), argtypes);
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, ms,
null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try {
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
emitBody(objx, gen, retClass, body);
Label end = gen.mark();
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, argtypes[lb.idx].getDescriptor(),
null, loopLabel, end, lb.idx);
}
} finally {
Var.popThreadBindings();
}
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
// generate the regular invoke, calling the static method
Method m = new Method(getMethodName(), OBJECT_TYPE, getArgTypes());
gen = new GeneratorAdapter(ACC_PUBLIC, m, null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
for (int i = 0; i < argtypes.length; i++) {
gen.loadArg(i);
HostExpr.emitUnboxArg(fn, gen, argclasses[i], "");
}
gen.invokeStatic(objx.objtype, ms);
gen.box(getReturnType(), "");
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
// TODOCLJ
}
public void doEmitPrim(ObjExpr fn, ClassVisitor cv) {
Type returnType;
if (retClass == double.class || retClass == long.class)
returnType = getReturnType();
else
returnType = OBJECT_TYPE;
Method ms = new Method("invokePrim", returnType, argtypes);
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_FINAL, ms,
null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < argLocals.count(); i++) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(printClass(argclasses[i]) + " "
+ ((LocalBinding) argLocals.nth(i)).print());
}
emitSource("public final " + printClass(retClass) + " invokePrim("
+ sb.toString() + ") {");
tab();
if (hasException) {
emitSource("try {");
tab();
}
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try {
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
String b = emitBody(objx, gen, retClass, body);
if (b != null) {
emitSource(b);
}
Label end = gen.mark();
gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel,
end, 0);
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, argtypes[lb.idx - 1].getDescriptor(),
null, loopLabel, end, lb.idx);
}
} finally {
Var.popThreadBindings();
}
if (hasException) {
untab();
emitSource("} catch (Exception ___e) {");
tab();
emitSource("throw Util.sneakyThrow(___e);");
untab();
emitSource("}");
}
untab();
emitSource("}");
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
// generate the regular invoke, calling the prim method
Method m = new Method(getMethodName(), OBJECT_TYPE, getArgTypes());
gen = new GeneratorAdapter(ACC_PUBLIC, m, null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
gen.loadThis();
sb = new StringBuilder();
StringBuilder args = new StringBuilder();
for (int i = 0; i < argtypes.length; i++) {
if (sb.length() > 0) {
sb.append(", ");
args.append(", ");
}
gen.loadArg(i);
String argname = ((LocalBinding) argLocals.nth(i)).print();
sb.append("Object " + argname);
args.append(HostExpr.emitUnboxArg(fn, gen, argclasses[i], argname));
}
emitSource("public Object invoke(" + sb.toString() + ") {");
tab();
gen.invokeInterface(Type.getType("L" + prim + ";"), ms);
emitSource("return "
+ gen.box(getReturnType(), "invokePrim(" + args + ")") + ";");
untab();
emitSource("}");
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
// TODOCLJ
}
public void doEmit(ObjExpr fn, ClassVisitor cv) {
Method m = new Method(getMethodName(), getReturnType(), getArgTypes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < argLocals.count(); i++) {
if (sb.length() > 0) {
sb.append(", ");
}
LocalBinding b = (LocalBinding) argLocals.get(i);
sb.append(printClass(getArgTypes()[i]) + " " + b.print());
}
emitSource("public " + printClass(getReturnType()) + " "
+ getMethodName() + "(" + sb.toString() + ") {");
tab();
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, m, null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
if (hasException) {
emitSource("try {");
tab();
}
if (hasRecur) {
emitSource("while(true) {");
tab();
}
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try {
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
String r = body.emit(C.RETURN, fn, gen);
if (r != null) { // handle throw
emitSource(r);
}
Label end = gen.mark();
gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel,
end, 0);
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null,
loopLabel, end, lb.idx);
}
} finally {
Var.popThreadBindings();
}
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
if (hasRecur) {
untab();
emitSource("}");
}
if (hasException) {
untab();
emitSource("} catch (Exception ___e) {");
tab();
emitSource("throw Util.sneakyThrow(___e);");
untab();
emitSource("}");
}
untab();
emitSource("}");
}
public final PersistentVector reqParms() {
return reqParms;
}
public final LocalBinding restParm() {
return restParm;
}
boolean isVariadic() {
return restParm != null;
}
int numParams() {
return reqParms.count() + (isVariadic() ? 1 : 0);
}
String getMethodName() {
return isVariadic() ? "doInvoke" : "invoke";
}
Type getReturnType() {
if (prim != null) // objx.isStatic)
return Type.getType(retClass);
return OBJECT_TYPE;
}
Type[] getArgTypes() {
if (isVariadic() && reqParms.count() == MAX_POSITIONAL_ARITY) {
Type[] ret = new Type[MAX_POSITIONAL_ARITY + 1];
for (int i = 0; i < MAX_POSITIONAL_ARITY + 1; i++)
ret[i] = OBJECT_TYPE;
return ret;
}
return ARG_TYPES[numParams()];
}
void emitClearLocals(GeneratorAdapter gen) {
// for(int i = 1; i < numParams() + 1; i++)
// {
// if(!localsUsedInCatchFinally.contains(i))
// {
// gen.visitInsn(Opcodes.ACONST_NULL);
// gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);
// }
// }
// for(int i = numParams() + 1; i < maxLocal + 1; i++)
// {
// if(!localsUsedInCatchFinally.contains(i))
// {
// LocalBinding b = (LocalBinding) RT.get(indexlocals, i);
// if(b == null || maybePrimitiveType(b.init) == null)
// {
// gen.visitInsn(Opcodes.ACONST_NULL);
// gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);
// }
// }
// }
// if(((FnExpr)objx).onceOnly)
// {
// objx.emitClearCloses(gen);
// }
}
}
abstract public static class ObjMethod {
// when closures are defined inside other closures,
// the closed over locals need to be propagated to the enclosing objx
public final ObjMethod parent;
boolean hasRecur;
boolean hasException;
// localbinding->localbinding
IPersistentMap locals = null;
// num->localbinding
IPersistentMap indexlocals = null;
Expr body = null;
ObjExpr objx;
PersistentVector argLocals;
int maxLocal = 0;
int line;
int column;
PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY;
protected IPersistentMap methodMeta;
public final IPersistentMap locals() {
return locals;
}
public final Expr body() {
return body;
}
public final ObjExpr objx() {
return objx;
}
public final PersistentVector argLocals() {
return argLocals;
}
public final int maxLocal() {
return maxLocal;
}
public final int line() {
return line;
}
public final int column() {
return column;
}
public ObjMethod(ObjExpr objx, ObjMethod parent) {
this.parent = parent;
this.objx = objx;
}
static String emitBody(ObjExpr objx, GeneratorAdapter gen, Class retClass,
Expr body) {
String ret;
MaybePrimitiveExpr be = (MaybePrimitiveExpr) body;
if (Util.isPrimitive(retClass) && be.canEmitPrimitive()) {
Class bc = maybePrimitiveType(be);
if (bc == retClass)
ret = be.emitUnboxed(C.RETURN, objx, gen);
else if (retClass == long.class && bc == int.class) {
ret = be.emitUnboxed(C.RETURN, objx, gen);
gen.visitInsn(I2L);
} else if (retClass == double.class && bc == float.class) {
ret = be.emitUnboxed(C.RETURN, objx, gen);
gen.visitInsn(F2D);
} else if (retClass == int.class && bc == long.class) {
Var.pushThreadBindings(RT.map(RETURN_TYPE, int.class));
ret = be.emitUnboxed(C.RETURN, objx, gen);
Var.popThreadBindings();
gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
} else if (retClass == float.class && bc == double.class) {
ret = be.emitUnboxed(C.RETURN, objx, gen);
gen.visitInsn(D2F);
} else
throw new IllegalArgumentException(
"Mismatched primitive return, expected: " + retClass + ", had: "
+ be.getJavaClass());
} else {
Var.pushThreadBindings(RT.map(RETURN_TYPE, retClass));
ret = body.emit(C.RETURN, objx, gen);
Var.popThreadBindings();
if (retClass == void.class) {
gen.pop();
} else {
gen.unbox(Type.getType(retClass));
}
}
return ret;
}
abstract int numParams();
abstract String getMethodName();
abstract Type getReturnType();
abstract Type[] getArgTypes();
public void emit(ObjExpr fn, ClassVisitor cv) {
Method m = new Method(getMethodName(), getReturnType(), getArgTypes());
StringBuilder params = new StringBuilder();
int pos = 0;
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
if (params.length() > 0) {
params.append(", ");
}
params.append(printClass(getArgTypes()[pos]) + " " + lb.name + lb.idx);
pos++;
}
emitSource("public " + printClass(getReturnType()) + " "
+ getMethodName() + "(" + params + ") {");
tab();
if (hasException) {
emitSource("try {");
tab();
}
if (hasRecur) {
emitSource("while(true) {");
tab();
}
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, m, null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try {
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
emitSource(body.emit(C.RETURN, fn, gen));
Label end = gen.mark();
gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel,
end, 0);
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null,
loopLabel, end, lb.idx);
}
} finally {
Var.popThreadBindings();
}
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
if (hasRecur) {
untab();
emitSource("}");
}
if (hasException) {
untab();
emitSource("} catch (Exception ___e) {");
tab();
emitSource("throw Util.sneakyThrow(___e);");
untab();
emitSource("}");
}
untab();
emitSource("}");
}
void emitClearLocals(GeneratorAdapter gen) {
}
void emitClearLocalsOld(GeneratorAdapter gen) {
for (int i = 0; i < argLocals.count(); i++) {
LocalBinding lb = (LocalBinding) argLocals.nth(i);
if (!localsUsedInCatchFinally.contains(lb.idx)
&& lb.getPrimitiveType() == null) {
gen.visitInsn(Opcodes.ACONST_NULL);
gen.storeArg(lb.idx - 1);
}
}
// for(int i = 1; i < numParams() + 1; i++)
// {
// if(!localsUsedInCatchFinally.contains(i))
// {
// gen.visitInsn(Opcodes.ACONST_NULL);
// gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);
// }
// }
for (int i = numParams() + 1; i < maxLocal + 1; i++) {
if (!localsUsedInCatchFinally.contains(i)) {
LocalBinding b = (LocalBinding) RT.get(indexlocals, i);
if (b == null || maybePrimitiveType(b.init) == null) {
gen.visitInsn(Opcodes.ACONST_NULL);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), i);
}
}
}
}
}
public static class LocalBinding {
public final Symbol sym;
public final Symbol tag;
public Expr init;
public final int idx;
public final String name;
public final boolean isArg;
public final PathNode clearPathRoot;
public boolean canBeCleared = !RT
.booleanCast(getCompilerOption(disableLocalsClearingKey));
public boolean recurMistmatch = false;
public LocalBinding(int num, Symbol sym, Symbol tag, Expr init,
boolean isArg, PathNode clearPathRoot) {
if (maybePrimitiveType(init) != null && tag != null)
throw new UnsupportedOperationException(
"Can't type hint a local with a primitive initializer");
this.idx = num;
this.sym = sym;
this.tag = tag;
this.init = init;
this.isArg = isArg;
this.clearPathRoot = clearPathRoot;
name = munge(sym.name);
}
public boolean hasJavaClass() {
if (init != null && init.hasJavaClass()
&& Util.isPrimitive(init.getJavaClass())
&& !(init instanceof MaybePrimitiveExpr))
return false;
return tag != null || (init != null && init.hasJavaClass());
}
public Class getJavaClass() {
return tag != null ? HostExpr.tagToClass(tag) : init.getJavaClass();
}
public Class getPrimitiveType() {
return maybePrimitiveType(init);
}
public String print() {
if (idx == -1) {
return name;
} else {
return name + idx;
}
}
}
public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr,
AssignableExpr {
public final LocalBinding b;
public final Symbol tag;
public final PathNode clearPath;
public final PathNode clearRoot;
public boolean shouldClear = false;
public LocalBindingExpr(LocalBinding b, Symbol tag) {
if (b.getPrimitiveType() != null && tag != null)
throw new UnsupportedOperationException(
"Can't type hint a primitive local");
this.b = b;
this.tag = tag;
this.clearPath = (PathNode) CLEAR_PATH.get();
this.clearRoot = (PathNode) CLEAR_ROOT.get();
IPersistentCollection sites = (IPersistentCollection) RT.get(
CLEAR_SITES.get(), b);
if (b.idx > 0) {
// Object dummy;
if (sites != null) {
for (ISeq s = sites.seq(); s != null; s = s.next()) {
LocalBindingExpr o = (LocalBindingExpr) s.first();
PathNode common = commonPath(clearPath, o.clearPath);
if (common != null && common.type == PATHTYPE.PATH)
o.shouldClear = false;
// else
// dummy = null;
}
}
if (clearRoot == b.clearPathRoot) {
this.shouldClear = true;
sites = RT.conj(sites, this);
CLEAR_SITES.set(RT.assoc(CLEAR_SITES.get(), b, sites));
}
// else
// dummy = null;
}
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval locals");
}
public boolean canEmitPrimitive() {
return b.getPrimitiveType() != null;
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
return wrap(context, objx.emitUnboxedLocal(gen, b));
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
if (context != C.STATEMENT)
return wrap(context, objx.emitLocal(gen, b, shouldClear));
return "";
}
public Object evalAssign(Expr val) {
throw new UnsupportedOperationException("Can't eval locals");
}
public String emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val) {
objx.emitAssignLocal(gen, b, val);
if (context != C.STATEMENT)
return objx.emitLocal(gen, b, false);
return "";
}
public boolean hasJavaClass() {
return tag != null || b.hasJavaClass();
}
public Class getJavaClass() {
if (tag != null)
return HostExpr.tagToClass(tag);
return b.getJavaClass();
}
}
public static class BodyExpr implements Expr, MaybePrimitiveExpr {
PersistentVector exprs;
public final PersistentVector exprs() {
return exprs;
}
public BodyExpr(PersistentVector exprs) {
this.exprs = exprs;
}
static class Parser implements IParser {
public Expr parse(C context, Object frms) {
ISeq forms = (ISeq) frms;
if (Util.equals(RT.first(forms), DO))
forms = RT.next(forms);
PersistentVector exprs = PersistentVector.EMPTY;
for (; forms != null; forms = forms.next()) {
Expr e = (context != C.EVAL && (context == C.STATEMENT || forms
.next() != null)) ? analyze(C.STATEMENT, forms.first())
: analyze(context, forms.first());
exprs = exprs.cons(e);
}
if (exprs.count() == 0)
exprs = exprs.cons(NIL_EXPR);
return new BodyExpr(exprs);
}
}
public Object eval() {
Object ret = null;
for (Object o : exprs) {
Expr e = (Expr) o;
ret = e.eval();
}
return ret;
}
public boolean canEmitPrimitive() {
return lastExpr() instanceof MaybePrimitiveExpr
&& ((MaybePrimitiveExpr) lastExpr()).canEmitPrimitive();
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
for (int i = 0; i < exprs.count() - 1; i++) {
Expr e = (Expr) exprs.nth(i);
String emit = e.emit(C.STATEMENT, objx, gen);
if (emit == null) {
return null;
}
emitSource(emit);
}
MaybePrimitiveExpr last = (MaybePrimitiveExpr) exprs
.nth(exprs.count() - 1);
return last.emitUnboxed(context, objx, gen);
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
for (int i = 0; i < exprs.count() - 1; i++) {
Expr e = (Expr) exprs.nth(i);
String emit = e.emit(C.STATEMENT, objx, gen);
if (emit == null) {
return null;
}
emitSource(emit);
}
Expr last = (Expr) exprs.nth(exprs.count() - 1);
return last.emit(context, objx, gen);
}
public boolean hasJavaClass() {
return lastExpr().hasJavaClass();
}
public Class getJavaClass() {
return lastExpr().getJavaClass();
}
private Expr lastExpr() {
return (Expr) exprs.nth(exprs.count() - 1);
}
}
public static class BindingInit {
LocalBinding binding;
Expr init;
public final LocalBinding binding() {
return binding;
}
public final Expr init() {
return init;
}
public BindingInit(LocalBinding binding, Expr init) {
this.binding = binding;
this.init = init;
}
}
public static class LetFnExpr implements Expr {
public final PersistentVector bindingInits;
public final Expr body;
public LetFnExpr(PersistentVector bindingInits, Expr body) {
this.bindingInits = bindingInits;
this.body = body;
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
// (letfns* [var (fn [args] body) ...] body...)
if (!(RT.second(form) instanceof IPersistentVector))
throw new IllegalArgumentException(
"Bad binding form, expected vector");
IPersistentVector bindings = (IPersistentVector) RT.second(form);
if ((bindings.count() % 2) != 0)
throw new IllegalArgumentException(
"Bad binding form, expected matched symbol expression pairs");
ISeq body = RT.next(RT.next(form));
if (context == C.EVAL)
return analyze(context,
RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref()
// ,NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref()
);
try {
Var.pushThreadBindings(dynamicBindings);
// pre-seed env (like Lisp labels)
PersistentVector lbs = PersistentVector.EMPTY;
for (int i = 0; i < bindings.count(); i += 2) {
if (!(bindings.nth(i) instanceof Symbol))
throw new IllegalArgumentException(
"Bad binding form, expected symbol, got: " + bindings.nth(i));
Symbol sym = (Symbol) bindings.nth(i);
if (sym.getNamespace() != null)
throw Util.runtimeException("Can't let qualified name: " + sym);
LocalBinding lb = registerLocal(sym, tagOf(sym), null, false);
lb.canBeCleared = false;
lbs = lbs.cons(lb);
}
PersistentVector bindingInits = PersistentVector.EMPTY;
for (int i = 0; i < bindings.count(); i += 2) {
Symbol sym = (Symbol) bindings.nth(i);
Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name);
LocalBinding lb = (LocalBinding) lbs.nth(i / 2);
lb.init = init;
BindingInit bi = new BindingInit(lb, init);
bindingInits = bindingInits.cons(bi);
}
return new LetFnExpr(bindingInits, (new BodyExpr.Parser()).parse(
context, body));
} finally {
Var.popThreadBindings();
}
}
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval letfns");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
for (int i = 0; i < bindingInits.count(); i++) {
BindingInit bi = (BindingInit) bindingInits.nth(i);
gen.visitInsn(Opcodes.ACONST_NULL);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
}
String r = registerTemp();
if (context == C.EXPRESSION) {
emitSource("Object " + r + ";");
}
IPersistentSet lbset = PersistentHashSet.EMPTY;
for (int i = 0; i < bindingInits.count(); i++) {
BindingInit bi = (BindingInit) bindingInits.nth(i);
lbset = (IPersistentSet) lbset.cons(bi.binding);
String val = bi.init.emit(C.EXPRESSION, objx, gen);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
emitSource("IFn " + bi.binding.print() + " = " + val + ";");
}
for (int i = 0; i < bindingInits.count(); i++) {
BindingInit bi = (BindingInit) bindingInits.nth(i);
ObjExpr fe = (ObjExpr) bi.init;
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), bi.binding.idx);
fe.emitLetFnInits(gen, objx, lbset);
}
Label loopLabel = gen.mark();
emitAssigRet(context, r, body.emit(context, objx, gen));
Label end = gen.mark();
// gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel,
// end, 0);
for (ISeq bis = bindingInits.seq(); bis != null; bis = bis.next()) {
BindingInit bi = (BindingInit) bis.first();
String lname = bi.binding.name;
if (lname.endsWith("__auto__"))
lname += RT.nextID();
Class primc = maybePrimitiveType(bi.init);
if (primc != null)
gen.visitLocalVariable(lname, Type.getDescriptor(primc), null,
loopLabel, end, bi.binding.idx);
else
gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel,
end, bi.binding.idx);
}
return (C.EXPRESSION == context ? r : "");
}
public boolean hasJavaClass() {
return body.hasJavaClass();
}
public Class getJavaClass() {
return body.getJavaClass();
}
}
public static class LetExpr implements Expr, MaybePrimitiveExpr {
public final PersistentVector bindingInits;
public final Expr body;
public final boolean isLoop;
public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop) {
this.bindingInits = bindingInits;
this.body = body;
this.isLoop = isLoop;
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
// (let [var val var2 val2 ...] body...)
boolean isLoop = RT.first(form).equals(LOOP);
if (!(RT.second(form) instanceof IPersistentVector))
throw new IllegalArgumentException(
"Bad binding form, expected vector");
IPersistentVector bindings = (IPersistentVector) RT.second(form);
if ((bindings.count() % 2) != 0)
throw new IllegalArgumentException(
"Bad binding form, expected matched symbol expression pairs");
ISeq body = RT.next(RT.next(form));
if (context == C.EVAL || (context == C.EXPRESSION && isLoop))
return analyze(context,
RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
ObjMethod method = (ObjMethod) METHOD.deref();
boolean methodHasRecur = method.hasRecur;
IPersistentMap backupMethodLocals = method.locals;
IPersistentMap backupMethodIndexLocals = method.indexlocals;
IPersistentVector recurMismatches = PersistentVector.EMPTY;
for (int i = 0; i < bindings.count() / 2; i++) {
recurMismatches = recurMismatches.cons(RT.F);
}
// may repeat once for each binding with a mismatch, return breaks
while (true) {
IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref()
// ,NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref()
);
method.locals = backupMethodLocals;
method.indexlocals = backupMethodIndexLocals;
PathNode looproot = new PathNode(PATHTYPE.PATH,
(PathNode) CLEAR_PATH.get());
PathNode clearroot = new PathNode(PATHTYPE.PATH, looproot);
PathNode clearpath = new PathNode(PATHTYPE.PATH, looproot);
if (isLoop)
dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null);
try {
Var.pushThreadBindings(dynamicBindings);
PersistentVector bindingInits = PersistentVector.EMPTY;
PersistentVector loopLocals = PersistentVector.EMPTY;
for (int i = 0; i < bindings.count(); i += 2) {
if (!(bindings.nth(i) instanceof Symbol))
throw new IllegalArgumentException(
"Bad binding form, expected symbol, got: "
+ bindings.nth(i));
Symbol sym = (Symbol) bindings.nth(i);
if (sym.getNamespace() != null)
throw Util.runtimeException("Can't let qualified name: " + sym);
Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name);
if (isLoop) {
if (recurMismatches != null
&& RT.booleanCast(recurMismatches.nth(i / 2))) {
init = new StaticMethodExpr("", 0, 0, null, RT.class, "box",
RT.vector(init));
if (RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
RT.errPrintWriter().println("Auto-boxing loop arg: " + sym);
} else if (maybePrimitiveType(init) == int.class)
init = new StaticMethodExpr("", 0, 0, null, RT.class,
"longCast", RT.vector(init));
else if (maybePrimitiveType(init) == float.class)
init = new StaticMethodExpr("", 0, 0, null, RT.class,
"doubleCast", RT.vector(init));
}
// sequential enhancement of env (like Lisp let*)
try {
if (isLoop) {
Var.pushThreadBindings(RT.map(CLEAR_PATH, clearpath,
CLEAR_ROOT, clearroot, NO_RECUR, null));
}
LocalBinding lb = registerLocal(sym, tagOf(sym), init, false);
BindingInit bi = new BindingInit(lb, init);
bindingInits = bindingInits.cons(bi);
if (isLoop)
loopLocals = loopLocals.cons(lb);
} finally {
if (isLoop)
Var.popThreadBindings();
}
}
if (isLoop)
LOOP_LOCALS.set(loopLocals);
Expr bodyExpr;
boolean moreMismatches = false;
try {
if (isLoop) {
Var.pushThreadBindings(RT.map(CLEAR_PATH, clearpath,
CLEAR_ROOT, clearroot, NO_RECUR, null));
}
bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN
: context, body);
} finally {
if (isLoop) {
Var.popThreadBindings();
for (int i = 0; i < loopLocals.count(); i++) {
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
if (lb.recurMistmatch) {
recurMismatches = (IPersistentVector) recurMismatches
.assoc(i, RT.T);
moreMismatches = true;
}
}
}
}
if (!moreMismatches)
return new LetExpr(bindingInits, bodyExpr, isLoop);
} finally {
if (isLoop) {
// Loop doesn't affect hasRecur
method.hasRecur = methodHasRecur;
}
Var.popThreadBindings();
}
}
}
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval let/loop");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
return doEmit(context, objx, gen, false);
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
return doEmit(context, objx, gen, true);
}
public String doEmit(C context, ObjExpr objx, GeneratorAdapter gen,
boolean emitUnboxed) {
String r = null;
if (context == C.EXPRESSION) {
r = registerTemp();
emitSource("Object " + r + " = null;");
}
if (!emitUnboxed) {
emitSource("{");
tab();
}
HashMap bindingLabels = new HashMap();
for (int i = 0; i < bindingInits.count(); i++) {
BindingInit bi = (BindingInit) bindingInits.nth(i);
Class primc = maybePrimitiveType(bi.init);
String val;
boolean typed = true;
if (primc != null) {
val = ((MaybePrimitiveExpr) bi.init).emitUnboxed(C.EXPRESSION, objx,
gen);
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE),
bi.binding.idx);
emitSource((typed ? printClass(primc) + " " : "")
+ bi.binding.print() + " = " + val + ";");
} else {
val = bi.init.emit(C.EXPRESSION, objx, gen);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE),
bi.binding.idx);
emitSource((typed ? "Object " : "") + bi.binding.print() + " = "
+ val + ";");
}
bindingLabels.put(bi, gen.mark());
}
if (isLoop) {
emitSource("while(true) {");
tab();
}
Label loopLabel = gen.mark();
String v;
if (isLoop) {
try {
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel));
if (emitUnboxed) {
v = ((MaybePrimitiveExpr) body).emitUnboxed(context, objx, gen);
} else {
v = body.emit(context, objx, gen);
}
} finally {
Var.popThreadBindings();
}
} else {
if (emitUnboxed) {
v = ((MaybePrimitiveExpr) body).emitUnboxed(context, objx, gen);
} else {
v = body.emit(context, objx, gen);
}
}
if (v != null) {
if (emitUnboxed) {
r = v;
} else {
emitAssigRet(context, r, v);
}
} else if (emitUnboxed) {
r = "null";
}
Label end = gen.mark();
// gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel,
// end, 0);
for (ISeq bis = bindingInits.seq(); bis != null; bis = bis.next()) {
BindingInit bi = (BindingInit) bis.first();
String lname = bi.binding.name;
if (lname.endsWith("__auto__"))
lname += RT.nextID();
Class primc = maybePrimitiveType(bi.init);
if (primc != null)
gen.visitLocalVariable(lname, Type.getDescriptor(primc), null,
bindingLabels.get(bi), end, bi.binding.idx);
else
gen.visitLocalVariable(lname, "Ljava/lang/Object;", null,
bindingLabels.get(bi), end, bi.binding.idx);
}
if (isLoop) {
if (C.RETURN != context) {
emitSource("break;");
}
untab();
emitSource("}");
}
if (!emitUnboxed) {
untab();
emitSource("}");
}
return (context == C.EXPRESSION || emitUnboxed ? r : (v == null ? null
: ""));
}
public boolean hasJavaClass() {
return body.hasJavaClass();
}
public Class getJavaClass() {
return body.getJavaClass();
}
public boolean canEmitPrimitive() {
return body instanceof MaybePrimitiveExpr
&& ((MaybePrimitiveExpr) body).canEmitPrimitive();
}
}
public static class RecurExpr implements Expr, MaybePrimitiveExpr {
public final IPersistentVector args;
public final IPersistentVector loopLocals;
final int line;
final int column;
final String source;
public RecurExpr(IPersistentVector loopLocals, IPersistentVector args,
int line, int column, String source) {
this.loopLocals = loopLocals;
this.args = args;
this.line = line;
this.column = column;
this.source = source;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval recur");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
Label loopLabel = (Label) LOOP_LABEL.deref();
String val = null;
if (loopLabel == null)
throw new IllegalStateException();
Expr last = null;
ArrayList auxs = new ArrayList();
for (int i = 0; i < loopLocals.count(); i++) {
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
Expr arg = (Expr) args.nth(i);
last = arg;
if (lb.getPrimitiveType() != null) {
Class primc = lb.getPrimitiveType();
final Class pc = maybePrimitiveType(arg);
if (pc == primc)
val = ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx,
gen);
else if (primc == long.class && pc == int.class) {
val = ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx,
gen);
gen.visitInsn(I2L);
} else if (primc == double.class && pc == float.class) {
val = ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx,
gen);
gen.visitInsn(F2D);
} else if (primc == int.class && pc == long.class) {
val = ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx,
gen);
gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
val = "RT.intCast(" + val + ")";
} else if (primc == float.class && pc == double.class) {
val = ((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx,
gen);
gen.visitInsn(D2F);
} else {
throw new IllegalArgumentException(
" recur arg for primitive local: "
+ lb.name
+ " is not matching primitive, had: "
+ (arg.hasJavaClass() ? arg.getJavaClass().getName()
: "Object") + ", needed: " + primc.getName());
}
} else {
val = arg.emit(C.EXPRESSION, objx, gen);
}
if (!lb.print().equals(val)) {
Class type = lb.getPrimitiveType();
emitSource(printClass(type == null ? Object.class : type) + " "
+ lb.print() + "___aux = " + val + ";");
auxs.add(lb.print());
}
}
for (String a : auxs) {
emitSource(a + " = " + a + "___aux;");
}
for (int i = loopLocals.count() - 1; i >= 0; i--) {
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
Class primc = lb.getPrimitiveType();
if (lb.isArg)
gen.storeArg(lb.idx - (objx.isStatic ? 0 : 1));
else {
if (primc != null)
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE),
lb.idx);
else
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);
}
}
gen.goTo(loopLabel);
return "continue;";
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return RECUR_CLASS;
}
static class Parser implements IParser {
public Expr parse(C context, Object frm) {
int line = lineDeref();
int column = columnDeref();
String source = (String) SOURCE.deref();
ISeq form = (ISeq) frm;
IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref();
if (context != C.RETURN || loopLocals == null)
throw new UnsupportedOperationException(
"Can only recur from tail position");
if (NO_RECUR.deref() != null)
throw new UnsupportedOperationException("Cannot recur across try");
ObjMethod objmethod = (ObjMethod) METHOD.deref();
objmethod.hasRecur = true;
PersistentVector args = PersistentVector.EMPTY;
for (ISeq s = RT.seq(form.next()); s != null; s = s.next()) {
args = args.cons(analyze(C.EXPRESSION, s.first()));
}
if (args.count() != loopLocals.count())
throw new IllegalArgumentException(String.format(
"Mismatched argument count to recur, expected: %d args, got: %d",
loopLocals.count(), args.count()));
for (int i = 0; i < loopLocals.count(); i++) {
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
Class primc = lb.getPrimitiveType();
if (primc != null) {
boolean mismatch = false;
final Class pc = maybePrimitiveType((Expr) args.nth(i));
if (primc == long.class) {
if (!(pc == long.class || pc == int.class || pc == short.class
|| pc == char.class || pc == byte.class))
mismatch = true;
} else if (primc == double.class) {
if (!(pc == double.class || pc == float.class))
mismatch = true;
}
if (mismatch) {
lb.recurMistmatch = true;
if (RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
RT.errPrintWriter().println(
source + ":" + line + " recur arg for primitive local: "
+ lb.name + " is not matching primitive, had: "
+ (pc != null ? pc.getName() : "Object") + ", needed: "
+ primc.getName());
}
}
}
return new RecurExpr(loopLocals, args, line, column, source);
}
}
public boolean canEmitPrimitive() {
return true;
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
return emit(context, objx, gen);
}
}
private static LocalBinding registerLocal(Symbol sym, Symbol tag, Expr init,
boolean isArg) {
int num = getAndIncLocalNum();
LocalBinding b = new LocalBinding(num, sym, tag, init, isArg,
clearPathRoot());
IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.deref();
LOCAL_ENV.set(RT.assoc(localsMap, b.sym, b));
ObjMethod method = (ObjMethod) METHOD.deref();
method.locals = (IPersistentMap) RT.assoc(method.locals, b, b);
method.indexlocals = (IPersistentMap) RT.assoc(method.indexlocals, num, b);
return b;
}
static int temp = 0;
private static String registerTemp() {
return "__r" + temp++;
}
private static int getAndIncLocalNum() {
int num = ((Number) NEXT_LOCAL_NUM.deref()).intValue();
ObjMethod m = (ObjMethod) METHOD.deref();
if (num > m.maxLocal)
m.maxLocal = num;
NEXT_LOCAL_NUM.set(num + 1);
return num;
}
public static Expr analyze(C context, Object form) {
return analyze(context, form, null);
}
private static Expr analyze(C context, Object form, String name) {
return analyze(context, form, name, null);
}
private static Expr analyze(C context, Object form, String name,
Object defContext) {
// todo symbol macro expansion?
try {
if (form instanceof LazySeq) {
form = RT.seq(form);
if (form == null)
form = PersistentList.EMPTY;
}
if (form == null)
return NIL_EXPR;
else if (form == Boolean.TRUE)
return TRUE_EXPR;
else if (form == Boolean.FALSE)
return FALSE_EXPR;
Class fclass = form.getClass();
if (fclass == Symbol.class)
return analyzeSymbol((Symbol) form);
else if (fclass == Keyword.class)
return registerKeyword((Keyword) form);
else if (form instanceof Number)
return NumberExpr.parse((Number) form);
else if (fclass == String.class)
return new StringExpr(((String) form).intern());
// else if(fclass == Character.class)
// return new CharExpr((Character) form);
else if (form instanceof IPersistentCollection
&& ((IPersistentCollection) form).count() == 0) {
Expr ret = new EmptyExpr(form);
if (RT.meta(form) != null)
ret = new MetaExpr(ret, MapExpr.parse(context == C.EVAL ? context
: C.EXPRESSION, ((IObj) form).meta()));
return ret;
} else if (form instanceof ISeq)
return analyzeSeq(context, (ISeq) form, name, defContext);
else if (form instanceof IPersistentVector)
return VectorExpr.parse(context, (IPersistentVector) form);
else if (form instanceof IRecord)
return new ConstantExpr(form);
else if (form instanceof IType)
return new ConstantExpr(form);
else if (form instanceof IPersistentMap)
return MapExpr.parse(context, (IPersistentMap) form);
else if (form instanceof IPersistentSet)
return SetExpr.parse(context, (IPersistentSet) form);
// else
// throw new UnsupportedOperationException();
return new ConstantExpr(form);
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(),
columnDeref(), e);
else
throw (CompilerException) e;
}
}
static public class CompilerException extends RuntimeException {
final public String source;
final public int line;
public CompilerException(String source, int line, int column,
Throwable cause) {
super(errorMsg(source, line, column, cause.toString()), cause);
this.source = source;
this.line = line;
}
public String toString() {
return getMessage();
}
}
static public Var isMacro(Object op) {
// no local macros for now
if (op instanceof Symbol && referenceLocal((Symbol) op) != null)
return null;
if (op instanceof Symbol || op instanceof Var) {
Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false,
false);
if (v != null && v.isMacro()) {
if (v.ns != currentNS() && !v.isPublic())
throw new IllegalStateException("var: " + v + " is not public");
return v;
}
}
return null;
}
static public IFn isInline(Object op, int arity) {
// no local inlines for now
if (op instanceof Symbol && referenceLocal((Symbol) op) != null)
return null;
if (op instanceof Symbol || op instanceof Var) {
Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false);
if (v != null) {
if (v.ns != currentNS() && !v.isPublic())
throw new IllegalStateException("var: " + v + " is not public");
IFn ret = (IFn) RT.get(v.meta(), inlineKey);
if (ret != null) {
IFn arityPred = (IFn) RT.get(v.meta(), inlineAritiesKey);
if (arityPred == null || RT.booleanCast(arityPred.invoke(arity)))
return ret;
}
}
}
return null;
}
public static boolean namesStaticMember(Symbol sym) {
return sym.ns != null && namespaceFor(sym) == null;
}
public static Object preserveTag(ISeq src, Object dst) {
Symbol tag = tagOf(src);
if (tag != null && dst instanceof IObj) {
IPersistentMap meta = RT.meta(dst);
return ((IObj) dst).withMeta((IPersistentMap) RT.assoc(meta, RT.TAG_KEY,
tag));
}
return dst;
}
public static Object macroexpand1(Object x) {
if (x instanceof ISeq) {
ISeq form = (ISeq) x;
Object op = RT.first(form);
if (isSpecial(op))
return x;
// macro expansion
Var v = isMacro(op);
if (v != null) {
try {
return v
.applyTo(RT.cons(form, RT.cons(LOCAL_ENV.get(), form.next())));
} catch (ArityException e) {
// hide the 2 extra params for a macro
throw new ArityException(e.actual - 2, e.name);
}
} else {
if (op instanceof Symbol) {
Symbol sym = (Symbol) op;
String sname = sym.name;
// (.substring s 2 5) => (. s substring 2 5)
if (sym.name.charAt(0) == '.') {
if (RT.length(form) < 2)
throw new IllegalArgumentException(
"Malformed member expression, expecting (.member target ...)");
Symbol meth = Symbol.intern(sname.substring(1));
Object target = RT.second(form);
if (HostExpr.maybeClass(target, false) != null) {
target = ((IObj) RT.list(IDENTITY, target)).withMeta(RT.map(
RT.TAG_KEY, CLASS));
}
return preserveTag(form,
RT.listStar(DOT, target, meth, form.next().next()));
} else if (namesStaticMember(sym)) {
Symbol target = Symbol.intern(sym.ns);
Class c = HostExpr.maybeClass(target, false);
if (c != null) {
Symbol meth = Symbol.intern(sym.name);
return preserveTag(form,
RT.listStar(DOT, target, meth, form.next()));
}
} else {
// (s.substring 2 5) => (. s substring 2 5)
// also (package.class.name ...) (. package.class name ...)
int idx = sname.lastIndexOf('.');
// if(idx > 0 && idx < sname.length() - 1)
// {
// Symbol target = Symbol.intern(sname.substring(0, idx));
// Symbol meth = Symbol.intern(sname.substring(idx + 1));
// return RT.listStar(DOT, target, meth, form.rest());
// }
// (StringBuilder. "foo") => (new StringBuilder "foo")
// else
if (idx == sname.length() - 1)
return RT.listStar(NEW, Symbol.intern(sname.substring(0, idx)),
form.next());
}
}
}
}
return x;
}
static Object macroexpand(Object form) {
Object exf = macroexpand1(form);
if (exf != form)
return macroexpand(exf);
return form;
}
private static Expr analyzeSeq(C context, ISeq form, String name,
Object defContext) {
Object line = lineDeref();
Object column = columnDeref();
if (RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
line = RT.meta(form).valAt(RT.LINE_KEY);
if (RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
column = RT.meta(form).valAt(RT.COLUMN_KEY);
Var.pushThreadBindings(RT.map(LINE, line, COLUMN, column));
try {
Object me = macroexpand1(form);
if (me != form)
return analyze(context, me, name, defContext);
Object op = RT.first(form);
if (op == null)
throw new IllegalArgumentException("Can't call nil");
IFn inline = isInline(op, RT.count(RT.next(form)));
if (inline != null)
return analyze(context,
preserveTag(form, inline.applyTo(RT.next(form))));
IParser p;
if (op.equals(FN))
return FnExpr.parse(context, form, name, defContext);
else if ((p = (IParser) specials.valAt(op)) != null)
return p.parse(context, form);
else
return InvokeExpr.parse(context, form);
} catch (Throwable e) {
if (!(e instanceof CompilerException))
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(),
columnDeref(), e);
else
throw (CompilerException) e;
} finally {
Var.popThreadBindings();
}
}
static String errorMsg(String source, int line, int column, String s) {
return String.format("%s, compiling:(%s:%d:%d)", s, source, line, column);
}
public static Object eval(Object form) {
return eval(form, true);
}
public static Object eval(Object form, boolean freshLoader) {
boolean createdLoader = false;
if (true)// !LOADER.isBound())
{
Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader()));
createdLoader = true;
}
try {
Object line = lineDeref();
Object column = columnDeref();
if (RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
line = RT.meta(form).valAt(RT.LINE_KEY);
if (RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
column = RT.meta(form).valAt(RT.COLUMN_KEY);
Var.pushThreadBindings(RT.map(LINE, line, COLUMN, column));
try {
form = macroexpand(form);
if (form instanceof ISeq && Util.equals(RT.first(form), DO)) {
ISeq s = RT.next(form);
for (; RT.next(s) != null; s = RT.next(s))
eval(RT.first(s), false);
return eval(RT.first(s), false);
} else if ((form instanceof IType)
|| (form instanceof IPersistentCollection && !(RT.first(form) instanceof Symbol && ((Symbol) RT
.first(form)).name.startsWith("def")))) {
ObjExpr fexpr = (ObjExpr) analyze(C.EXPRESSION,
RT.list(FN, PersistentVector.EMPTY, form), "eval" + RT.nextID());
IFn fn = (IFn) fexpr.eval();
return fn.invoke();
} else {
Expr expr = analyze(C.EVAL, form);
return expr.eval();
}
} finally {
Var.popThreadBindings();
}
}
finally {
if (createdLoader)
Var.popThreadBindings();
}
}
private static int registerConstant(Object o) {
if (!CONSTANTS.isBound())
return -1;
PersistentVector v = (PersistentVector) CONSTANTS.deref();
IdentityHashMap ids = (IdentityHashMap) CONSTANT_IDS
.deref();
Integer i = ids.get(o);
if (i != null)
return i;
CONSTANTS.set(RT.conj(v, o));
ids.put(o, v.count());
return v.count();
}
private static KeywordExpr registerKeyword(Keyword keyword) {
if (!KEYWORDS.isBound())
return new KeywordExpr(keyword);
IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.deref();
Object id = RT.get(keywordsMap, keyword);
if (id == null) {
KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword)));
}
return new KeywordExpr(keyword);
// KeywordExpr ke = (KeywordExpr) RT.get(keywordsMap, keyword);
// if(ke == null)
// KEYWORDS.set(RT.assoc(keywordsMap, keyword, ke = new
// KeywordExpr(keyword)));
// return ke;
}
private static int registerKeywordCallsite(Keyword keyword) {
if (!KEYWORD_CALLSITES.isBound())
throw new IllegalAccessError("KEYWORD_CALLSITES is not bound");
IPersistentVector keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES
.deref();
keywordCallsites = keywordCallsites.cons(keyword);
KEYWORD_CALLSITES.set(keywordCallsites);
return keywordCallsites.count() - 1;
}
private static int registerProtocolCallsite(Var v) {
if (!PROTOCOL_CALLSITES.isBound())
throw new IllegalAccessError("PROTOCOL_CALLSITES is not bound");
IPersistentVector protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES
.deref();
protocolCallsites = protocolCallsites.cons(v);
PROTOCOL_CALLSITES.set(protocolCallsites);
return protocolCallsites.count() - 1;
}
// private static void registerVarCallsite(Var v) {
// if (!VAR_CALLSITES.isBound())
// throw new IllegalAccessError("VAR_CALLSITES is not bound");
//
// IPersistentCollection varCallsites = (IPersistentCollection) VAR_CALLSITES
// .deref();
//
// varCallsites = varCallsites.cons(v);
// VAR_CALLSITES.set(varCallsites);
// // return varCallsites.count()-1;
// }
static ISeq fwdPath(PathNode p1) {
ISeq ret = null;
for (; p1 != null; p1 = p1.parent)
ret = RT.cons(p1, ret);
return ret;
}
static PathNode commonPath(PathNode n1, PathNode n2) {
ISeq xp = fwdPath(n1);
ISeq yp = fwdPath(n2);
if (RT.first(xp) != RT.first(yp))
return null;
while (RT.second(xp) != null && RT.second(xp) == RT.second(yp)) {
xp = xp.next();
yp = yp.next();
}
return (PathNode) RT.first(xp);
}
static void addAnnotation(Object visitor, IPersistentMap meta) {
if (meta != null && ADD_ANNOTATIONS.isBound())
ADD_ANNOTATIONS.invoke(visitor, meta);
}
static void addParameterAnnotation(Object visitor, IPersistentMap meta, int i) {
if (meta != null && ADD_ANNOTATIONS.isBound())
ADD_ANNOTATIONS.invoke(visitor, meta, i);
}
private static Expr analyzeSymbol(Symbol sym) {
Symbol tag = tagOf(sym);
if (sym.ns == null) // ns-qualified syms are always Vars
{
LocalBinding b = referenceLocal(sym);
if (b != null) {
return new LocalBindingExpr(b, tag);
}
} else {
if (namespaceFor(sym) == null) {
Symbol nsSym = Symbol.intern(sym.ns);
Class c = HostExpr.maybeClass(nsSym, false);
if (c != null) {
if (Reflector.getField(c, sym.name, true) != null)
return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name,
tag);
throw Util.runtimeException("Unable to find static field: "
+ sym.name + " in " + c);
}
}
}
// Var v = lookupVar(sym, false);
// Var v = lookupVar(sym, false);
// if(v != null)
// return new VarExpr(v, tag);
Object o = resolve(sym);
if (o instanceof Var) {
Var v = (Var) o;
if (isMacro(v) != null)
throw Util.runtimeException("Can't take value of a macro: " + v);
if (RT.booleanCast(RT.get(v.meta(), RT.CONST_KEY)))
return analyze(C.EXPRESSION, RT.list(QUOTE, v.get()));
registerVar(v);
return new VarExpr(v, tag);
} else if (o instanceof Class)
return new ConstantExpr(o);
else if (o instanceof Symbol)
return new UnresolvedVarExpr((Symbol) o);
throw Util.runtimeException("Unable to resolve symbol: " + sym
+ " in this context");
}
static String destubClassName(String className) {
// skip over prefix + '.' or '/'
if (className.startsWith(COMPILE_STUB_PREFIX))
return className.substring(COMPILE_STUB_PREFIX.length() + 1);
return className;
}
static Type getType(Class c) {
String descriptor = Type.getType(c).getDescriptor();
if (descriptor.startsWith("L"))
descriptor = "L" + destubClassName(descriptor.substring(1));
return Type.getType(descriptor);
}
static Object resolve(Symbol sym, boolean allowPrivate) {
return resolveIn(currentNS(), sym, allowPrivate);
}
static Object resolve(Symbol sym) {
return resolveIn(currentNS(), sym, false);
}
static Namespace namespaceFor(Symbol sym) {
return namespaceFor(currentNS(), sym);
}
static Namespace namespaceFor(Namespace inns, Symbol sym) {
// note, presumes non-nil sym.ns
// first check against currentNS' aliases...
Symbol nsSym = Symbol.intern(sym.ns);
Namespace ns = inns.lookupAlias(nsSym);
if (ns == null) {
// ...otherwise check the Namespaces map.
ns = Namespace.find(nsSym);
}
return ns;
}
static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) {
// note - ns-qualified vars must already exist
if (sym.ns != null) {
Namespace ns = namespaceFor(n, sym);
if (ns == null)
throw Util.runtimeException("No such namespace: " + sym.ns);
Var v = ns.findInternedVar(Symbol.intern(sym.name));
if (v == null)
throw Util.runtimeException("No such var: " + sym);
else if (v.ns != currentNS() && !v.isPublic() && !allowPrivate)
throw new IllegalStateException("var: " + sym + " is not public");
return v;
} else if (sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[') {
return RT.classForName(sym.name);
} else if (sym.equals(NS))
return RT.NS_VAR;
else if (sym.equals(IN_NS))
return RT.IN_NS_VAR;
else {
if (Util.equals(sym, COMPILE_STUB_SYM.get()))
return COMPILE_STUB_CLASS.get();
Object o = n.getMapping(sym);
if (o == null) {
if (RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref())) {
return sym;
} else {
throw Util.runtimeException("Unable to resolve symbol: " + sym
+ " in this context");
}
}
return o;
}
}
static public Object maybeResolveIn(Namespace n, Symbol sym) {
// note - ns-qualified vars must already exist
if (sym.ns != null) {
Namespace ns = namespaceFor(n, sym);
if (ns == null)
return null;
Var v = ns.findInternedVar(Symbol.intern(sym.name));
if (v == null)
return null;
return v;
} else if (sym.name.indexOf('.') > 0 && !sym.name.endsWith(".")
|| sym.name.charAt(0) == '[') {
return RT.classForName(sym.name);
} else if (sym.equals(NS))
return RT.NS_VAR;
else if (sym.equals(IN_NS))
return RT.IN_NS_VAR;
else {
Object o = n.getMapping(sym);
return o;
}
}
static Var lookupVar(Symbol sym, boolean internNew, boolean registerMacro) {
Var var = null;
// note - ns-qualified vars in other namespaces must already exist
if (sym.ns != null) {
Namespace ns = namespaceFor(sym);
if (ns == null)
return null;
// throw Util.runtimeException("No such namespace: " + sym.ns);
Symbol name = Symbol.intern(sym.name);
if (internNew && ns == currentNS())
var = currentNS().intern(name);
else
var = ns.findInternedVar(name);
} else if (sym.equals(NS))
var = RT.NS_VAR;
else if (sym.equals(IN_NS))
var = RT.IN_NS_VAR;
else {
// is it mapped?
Object o = currentNS().getMapping(sym);
if (o == null) {
// introduce a new var in the current ns
if (internNew)
var = currentNS().intern(Symbol.intern(sym.name));
} else if (o instanceof Var) {
var = (Var) o;
} else {
throw Util.runtimeException("Expecting var, but " + sym
+ " is mapped to " + o);
}
}
if (var != null && (!var.isMacro() || registerMacro))
registerVar(var);
return var;
}
static Var lookupVar(Symbol sym, boolean internNew) {
return lookupVar(sym, internNew, true);
}
private static void registerVar(Var var) {
if (!VARS.isBound())
return;
IPersistentMap varsMap = (IPersistentMap) VARS.deref();
Object id = RT.get(varsMap, var);
if (id == null) {
VARS.set(RT.assoc(varsMap, var, registerConstant(var)));
}
// if(varsMap != null && RT.get(varsMap, var) == null)
// VARS.set(RT.assoc(varsMap, var, var));
}
static Namespace currentNS() {
return (Namespace) RT.CURRENT_NS.deref();
}
static void closeOver(LocalBinding b, ObjMethod method) {
if (b != null && method != null) {
if (RT.get(method.locals, b) == null) {
method.objx.closes = (IPersistentMap) RT
.assoc(method.objx.closes, b, b);
closeOver(b, method.parent);
} else if (IN_CATCH_FINALLY.deref() != null) {
method.localsUsedInCatchFinally = (PersistentHashSet) method.localsUsedInCatchFinally
.cons(b.idx);
}
}
}
static LocalBinding referenceLocal(Symbol sym) {
if (!LOCAL_ENV.isBound())
return null;
LocalBinding b = (LocalBinding) RT.get(LOCAL_ENV.deref(), sym);
if (b != null) {
ObjMethod method = (ObjMethod) METHOD.deref();
closeOver(b, method);
}
return b;
}
private static Symbol tagOf(Object o) {
Object tag = RT.get(RT.meta(o), RT.TAG_KEY);
if (tag instanceof Symbol)
return (Symbol) tag;
else if (tag instanceof String)
return Symbol.intern(null, (String) tag);
return null;
}
public static Object loadFile(String file) throws IOException {
// File fo = new File(file);
// if(!fo.exists())
// return null;
FileInputStream f = new FileInputStream(file);
try {
return load(new InputStreamReader(f, RT.UTF8),
new File(file).getAbsolutePath(), (new File(file)).getName());
} finally {
f.close();
}
}
public static Object load(Reader rdr) {
return load(rdr, null, "NO_SOURCE_FILE");
}
static void consumeWhitespaces(LineNumberingPushbackReader pushbackReader) {
int ch = LispReader.read1(pushbackReader);
while(LispReader.isWhitespace(ch))
ch = LispReader.read1(pushbackReader);
LispReader.unread(pushbackReader, ch);
}
private static final Object OPTS_COND_ALLOWED = RT.mapUniqueKeys(LispReader.OPT_READ_COND, LispReader.COND_ALLOW);
private static Object readerOpts(String sourceName) {
if(sourceName != null && sourceName.endsWith(".cljc"))
return OPTS_COND_ALLOWED;
else
return null;
}
public static Object load(Reader rdr, String sourcePath, String sourceName) {
Object EOF = new Object();
Object ret = null;
LineNumberingPushbackReader pushbackReader = (rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr
: new LineNumberingPushbackReader(rdr);
consumeWhitespaces(pushbackReader);
Var.pushThreadBindings(RT.mapUniqueKeys(LOADER, RT.makeClassLoader(),
SOURCE_PATH, sourcePath, SOURCE, sourceName, METHOD, null, LOCAL_ENV,
null, LOOP_LOCALS, null, NEXT_LOCAL_NUM, 0, RT.READEVAL, RT.T,
RT.CURRENT_NS, RT.CURRENT_NS.deref(), LINE_BEFORE,
pushbackReader.getLineNumber(), COLUMN_BEFORE,
pushbackReader.getColumnNumber(), LINE_AFTER,
pushbackReader.getLineNumber(), COLUMN_AFTER,
pushbackReader.getColumnNumber(), RT.UNCHECKED_MATH,
RT.UNCHECKED_MATH.deref(), RT.WARN_ON_REFLECTION,
RT.WARN_ON_REFLECTION.deref(), RT.DATA_READERS, RT.DATA_READERS.deref()));
Object readerOpts = readerOpts(sourceName);
try {
for (Object r = LispReader.read(pushbackReader, false, EOF, false, readerOpts); r != EOF; r = LispReader
.read(pushbackReader, false, EOF, false, readerOpts)) {
consumeWhitespaces(pushbackReader);
LINE_AFTER.set(pushbackReader.getLineNumber());
COLUMN_AFTER.set(pushbackReader.getColumnNumber());
ret = eval(r, false);
LINE_BEFORE.set(pushbackReader.getLineNumber());
COLUMN_BEFORE.set(pushbackReader.getColumnNumber());
}
} catch (LispReader.ReaderException e) {
throw new CompilerException(sourcePath, e.line, e.column, e.getCause());
} catch (Throwable e) {
if(!(e instanceof CompilerException))
throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), e);
else
throw (CompilerException) e;
} finally {
Var.popThreadBindings();
}
return ret;
}
static public void writeClassFile(String internalName, byte[] bytecode)
throws IOException {
String genPath = (String) COMPILE_PATH.deref();
if (genPath == null)
throw Util.runtimeException("*compile-path* not set");
String[] dirs = internalName.split("/");
String p = genPath;
for (int i = 0; i < dirs.length - 1; i++) {
p += File.separator + dirs[i];
(new File(p)).mkdir();
}
String path = genPath + File.separator + internalName + ".class";
File cf = new File(path);
cf.createNewFile();
FileOutputStream cfs = new FileOutputStream(cf);
try {
cfs.write(bytecode);
cfs.flush();
cfs.getFD().sync();
} finally {
cfs.close();
}
}
private static String readFileAsString(String filePath) throws IOException {
StringBuffer fileData = new StringBuffer();
BufferedReader reader = new BufferedReader(new FileReader(filePath));
char[] buf = new char[1024];
int numRead = 0;
while ((numRead = reader.read(buf)) != -1) {
String readData = String.valueOf(buf, 0, numRead);
fileData.append(readData);
}
reader.close();
return fileData.toString();
}
static public void writeSourceFile(String internalName, String source)
throws IOException {
String genPath = (String) Compiler.SOURCE_GEN_PATH.deref();
if (genPath == null) {
genPath = "target/gen";
}
String[] dirs = internalName.split("/");
String path = genPath + File.separator + internalName + ".java";
File cf = new File(path);
cf.getParentFile().mkdirs();
if (!cf.exists()) {
cf.createNewFile();
} else {
if (readFileAsString(path).equals(source)) {
return;
}
}
FileOutputStream cfs = new FileOutputStream(cf);
try {
cfs.write(source.getBytes());
cfs.flush();
cfs.getFD().sync();
} finally {
cfs.close();
}
}
public static void pushNS() {
Var.pushThreadBindings(PersistentHashMap.create(
Var.intern(Symbol.intern("clojure.core"), Symbol.intern("*ns*"))
.setDynamic(), null));
}
public static void pushNSandLoader(ClassLoader loader) {
Var.pushThreadBindings(RT.map(
Var.intern(Symbol.intern("clojure.core"), Symbol.intern("*ns*"))
.setDynamic(), null, RT.FN_LOADER_VAR, loader, RT.READEVAL, RT.T));
}
public static ILookupThunk getLookupThunk(Object target, Keyword k) {
return null; // To change body of created methods use File | Settings | File
// Templates.
}
static void compile1(GeneratorAdapter gen, ObjExpr objx, Object form) {
Object line = lineDeref();
Object column = columnDeref();
if (RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
line = RT.meta(form).valAt(RT.LINE_KEY);
if (RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
column = RT.meta(form).valAt(RT.COLUMN_KEY);
Var.pushThreadBindings(RT.map(LINE, line, COLUMN, column, LOADER,
RT.makeClassLoader()));
try {
form = macroexpand(form);
if (form instanceof ISeq && Util.equals(RT.first(form), DO)) {
for (ISeq s = RT.next(form); s != null; s = RT.next(s)) {
compile1(gen, objx, RT.first(s));
}
} else {
Expr expr = analyze(C.EVAL, form);
// System.out.println(form);
objx.keywords = (IPersistentMap) KEYWORDS.deref();
objx.vars = (IPersistentMap) VARS.deref();
objx.constants = (PersistentVector) CONSTANTS.deref();
emitSource(expr.emit(C.STATEMENT, objx, gen)); // C.EXPRESSION??
expr.eval();
}
} finally {
Var.popThreadBindings();
}
}
public static Object compile(Reader rdr, String sourcePath, String sourceName)
throws IOException {
if (COMPILE_PATH.deref() == null)
throw Util.runtimeException("*compile-path* not set");
Object EOF = new Object();
Object ret = null;
LineNumberingPushbackReader pushbackReader = (rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr
: new LineNumberingPushbackReader(rdr);
Var.pushThreadBindings(RT.mapUniqueKeys(SOURCE_PATH, sourcePath, SOURCE,
sourceName, METHOD, null, LOCAL_ENV, null, LOOP_LOCALS, null,
NEXT_LOCAL_NUM, 0, RT.READEVAL, RT.T, RT.CURRENT_NS,
RT.CURRENT_NS.deref(), LINE_BEFORE, pushbackReader.getLineNumber(),
COLUMN_BEFORE, pushbackReader.getColumnNumber(), LINE_AFTER,
pushbackReader.getLineNumber(), COLUMN_AFTER,
pushbackReader.getColumnNumber(), CONSTANTS, PersistentVector.EMPTY,
CONSTANT_IDS, new IdentityHashMap(), KEYWORDS, PersistentHashMap.EMPTY,
VARS, PersistentHashMap.EMPTY, RT.UNCHECKED_MATH,
RT.UNCHECKED_MATH.deref(), RT.WARN_ON_REFLECTION,
RT.WARN_ON_REFLECTION.deref(), RT.DATA_READERS, RT.DATA_READERS.deref()
// ,LOADER, RT.makeClassLoader()
));
try {
// generate loader class
ObjExpr objx = new ObjExpr(null);
objx.internalName = sourcePath.replace(File.separator, "/").substring(0,
sourcePath.lastIndexOf('.'))
+ RT.LOADER_SUFFIX;
objx.objtype = Type.getObjectType(objx.internalName);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = cw;
int lastSlash = objx.internalName.lastIndexOf('/');
String className = objx.internalName.substring(lastSlash + 1);
String packageName = objx.internalName.substring(0, lastSlash)
.replaceAll("/", ".");
SourceWriter source = cw.getSc();
Var.pushThreadBindings(RT.mapUniqueKeys(SOURCE_WRITER, source));
emitSource("package " + packageName + ";");
emitSource();
emitSource("import clojure.lang.*;");
emitSource();
emitSource("@com.google.j2objc.annotations.ReflectionSupport(com.google.j2objc.annotations.ReflectionSupport.Level.NATIVE_ONLY)");
emitSource("public class " + className + " {");
tab();
cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, objx.internalName, null,
"java/lang/Object", null);
emitSource("public static void load() throws Exception {");
// static load method
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
Method.getMethod("void load ()"), null, null, cv);
tab();
gen.visitCode();
Object readerOpts = readerOpts(sourceName);
for (Object r = LispReader.read(pushbackReader, false, EOF, false, readerOpts); r != EOF; r = LispReader
.read(pushbackReader, false, EOF, false, readerOpts)) {
LINE_AFTER.set(pushbackReader.getLineNumber());
COLUMN_AFTER.set(pushbackReader.getColumnNumber());
compile1(gen, objx, r);
LINE_BEFORE.set(pushbackReader.getLineNumber());
COLUMN_BEFORE.set(pushbackReader.getColumnNumber());
}
// end of load
gen.returnValue();
gen.endMethod();
untab();
emitSource("}");
// static fields for constants
PersistentVector constants = PersistentVector.create(objx.usedConstants
.seq());
for (int j = 0; j < constants.count(); j++) {
int i = (Integer) constants.nth(j);
cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
objx.constantName(i), objx.constantType(i).getDescriptor(), null,
null);
emitSource("private static " + printClass(objx.constantRealType(i))
+ " " + objx.constantName(i) + ";");
}
final int INITS_PER = 100;
int numInits = constants.count() / INITS_PER;
if (constants.count() % INITS_PER != 0)
++numInits;
for (int n = 0; n < numInits; n++) {
GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC
+ ACC_STATIC, Method.getMethod("void __init" + n + "()"), null,
null, cv);
emitSource("static void __init" + n + "() {");
tab();
clinitgen.visitCode();
try {
Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));
for (int j = n * INITS_PER; j < constants.count()
&& j < (n + 1) * INITS_PER; j++) {
int i = (Integer) constants.nth(j);
String val = objx.emitValue(objx.constants.nth(i), clinitgen);
clinitgen.checkCast(objx.constantType(i));
clinitgen.putStatic(objx.objtype, objx.constantName(i),
objx.constantType(i));
emitSource(objx.constantName(i) + " = ("
+ printClass(objx.constantRealType(i)) + ")" + val + ";");
}
} finally {
Var.popThreadBindings();
}
untab();
emitSource("}");
clinitgen.returnValue();
clinitgen.endMethod();
}
emitSource("static {");
tab();
// static init for constants, keywords and vars
GeneratorAdapter clinitgen = new GeneratorAdapter(
ACC_PUBLIC + ACC_STATIC, Method.getMethod("void ()"), null,
null, cv);
clinitgen.visitCode();
Label startTry = clinitgen.newLabel();
Label endTry = clinitgen.newLabel();
Label end = clinitgen.newLabel();
Label finallyLabel = clinitgen.newLabel();
// if(objx.constants.count() > 0)
// {
// objx.emitConstants(clinitgen);
// }
for (int n = 0; n < numInits; n++) {
clinitgen.invokeStatic(objx.objtype,
Method.getMethod("void __init" + n + "()"));
emitSource("__init" + n + "();");
}
String iname = objx.internalName.replace('/', '.');
clinitgen.push(iname);
clinitgen.invokeStatic(RT_TYPE,
Method.getMethod("Class classForName(String)"));
clinitgen.invokeVirtual(CLASS_TYPE,
Method.getMethod("ClassLoader getClassLoader()"));
clinitgen.invokeStatic(Type.getType(Compiler.class),
Method.getMethod("void pushNSandLoader(ClassLoader)"));
clinitgen.mark(startTry);
clinitgen.invokeStatic(objx.objtype, Method.getMethod("void load()"));
clinitgen.mark(endTry);
clinitgen.invokeStatic(VAR_TYPE,
Method.getMethod("void popThreadBindings()"));
clinitgen.goTo(end);
clinitgen.mark(finallyLabel);
// exception should be on stack
clinitgen.invokeStatic(VAR_TYPE,
Method.getMethod("void popThreadBindings()"));
clinitgen.throwException();
clinitgen.mark(end);
clinitgen.visitTryCatchBlock(startTry, endTry, finallyLabel, null);
// end of static init
clinitgen.returnValue();
clinitgen.endMethod();
// end of class
cv.visitEnd();
emitSource("clojure.lang.Compiler.pushNSandLoader(" + iname
+ ".class.getClassLoader());");
emitSource("try {");
tab();
emitSource("load();");
untab();
emitSource("} catch (Exception ___x) {");
emitSource("throw new RuntimeException(___x);");
emitSource("} finally {");
tab();
emitSource("Var.popThreadBindings();");
untab();
emitSource("}");
untab();
emitSource("}");
untab();
emitSource("}");
Var.popThreadBindings();
writeClassFile(objx.internalName, cw.toByteArray());
writeSourceFile(objx.internalName, source.toString());
} catch (LispReader.ReaderException e) {
throw new CompilerException(sourcePath, e.line, e.column, e.getCause());
} finally {
Var.popThreadBindings();
}
return ret;
}
static public class NewInstanceExpr extends ObjExpr {
// IPersistentMap optionsMap = PersistentArrayMap.EMPTY;
IPersistentCollection methods;
Map mmap;
Map> covariants;
private IPersistentVector overrideMethods;
public NewInstanceExpr(Object tag) {
super(tag);
}
static class DeftypeParser implements IParser {
public Expr parse(C context, final Object frm) {
ISeq rform = (ISeq) frm;
// (deftype* tagname classname [fields] :implements [interfaces] :tag
// tagname methods*)
rform = RT.next(rform);
String tagname = ((Symbol) rform.first()).toString();
rform = rform.next();
Symbol classname = (Symbol) rform.first();
rform = rform.next();
IPersistentVector fields = (IPersistentVector) rform.first();
rform = rform.next();
IPersistentMap opts = PersistentHashMap.EMPTY;
while (rform != null && rform.first() instanceof Keyword) {
opts = opts.assoc(rform.first(), RT.second(rform));
rform = rform.next().next();
}
ObjExpr ret = build((IPersistentVector) RT.get(opts, implementsKey,
PersistentVector.EMPTY), fields, null, tagname, classname,
(Symbol) RT.get(opts, RT.TAG_KEY), rform, frm);
return ret;
}
}
static class ReifyParser implements IParser {
public Expr parse(C context, Object frm) {
// (reify this-name? [interfaces] (method-name [args] body)*)
ISeq form = (ISeq) frm;
ObjMethod enclosingMethod = (ObjMethod) METHOD.deref();
String basename = enclosingMethod != null ? (trimGenID(enclosingMethod.objx.name) + DOLLAR)
: (munge(currentNS().name.name) + DOLLAR);
String simpleName = "reify__" + RT.nextID();
String classname = basename + simpleName;
ISeq rform = RT.next(form);
IPersistentVector interfaces = ((IPersistentVector) RT.first(rform))
.cons(Symbol.intern("clojure.lang.IObj"));
rform = RT.next(rform);
ObjExpr ret = build(interfaces, null, null, classname,
Symbol.intern(classname), null, rform, frm);
if (frm instanceof IObj && ((IObj) frm).meta() != null)
return new MetaExpr(ret, MapExpr.parse(context == C.EVAL ? context
: C.EXPRESSION, ((IObj) frm).meta()));
else
return ret;
}
}
static ObjExpr build(IPersistentVector interfaceSyms,
IPersistentVector fieldSyms, Symbol thisSym, String tagName,
Symbol className, Symbol typeTag, ISeq methodForms, Object frm) {
NewInstanceExpr ret = new NewInstanceExpr(null);
ret.src = frm;
ret.name = className.toString();
ret.classMeta = RT.meta(className);
ret.internalName = ret.name.replace('.', '/');
ret.objtype = Type.getObjectType(ret.internalName);
if (thisSym != null)
ret.thisName = thisSym.name;
if (fieldSyms != null) {
IPersistentMap fmap = PersistentHashMap.EMPTY;
Object[] closesvec = new Object[2 * fieldSyms.count()];
for (int i = 0; i < fieldSyms.count(); i++) {
Symbol sym = (Symbol) fieldSyms.nth(i);
if (sym.name.equals("this")) {
throw new RuntimeException(
"A deftype/defrecord field cannot be named 'this'");
}
LocalBinding lb = new LocalBinding(-1, sym, null,
new MethodParamExpr(tagClass(tagOf(sym))), false, null);
fmap = fmap.assoc(sym, lb);
closesvec[i * 2] = lb;
closesvec[i * 2 + 1] = lb;
}
// todo - inject __meta et al into closes - when?
// use array map to preserve ctor order
ret.closes = new PersistentArrayMap(closesvec);
ret.fields = fmap;
for (int i = fieldSyms.count() - 1; i >= 0
&& (((Symbol) fieldSyms.nth(i)).name.equals("__meta") || ((Symbol) fieldSyms
.nth(i)).name.equals("__extmap")); --i)
ret.altCtorDrops++;
}
// todo - set up volatiles
// ret.volatiles = PersistentHashSet.create(RT.seq(RT.get(ret.optionsMap,
// volatileKey)));
PersistentVector interfaces = PersistentVector.EMPTY;
for (ISeq s = RT.seq(interfaceSyms); s != null; s = s.next()) {
Class c = (Class) resolve((Symbol) s.first());
if (!c.isInterface())
throw new IllegalArgumentException(
"only interfaces are supported, had: " + c.getName());
interfaces = interfaces.cons(c);
}
Class superClass = Object.class;
Map[] mc = gatherMethods(superClass, RT.seq(interfaces));
Map overrideables = mc[0];
Map covariants = mc[1];
Map parameters = mc[2];
ret.mmap = overrideables;
ret.covariants = covariants;
Map allmethods = new HashMap(overrideables);
String[] inames = interfaceNames(interfaces);
Class stub = compileStub(slashname(superClass), ret, inames, frm);
Symbol thistag = Symbol.intern(null, stub.getName());
try {
Var.pushThreadBindings(RT.mapUniqueKeys(CONSTANTS,
PersistentVector.EMPTY, CONSTANT_IDS, new IdentityHashMap(),
KEYWORDS, PersistentHashMap.EMPTY, VARS, PersistentHashMap.EMPTY,
KEYWORD_CALLSITES, PersistentVector.EMPTY, PROTOCOL_CALLSITES,
PersistentVector.EMPTY, VAR_CALLSITES, emptyVarCallSites(),
NO_RECUR, null, Compiler.NEXT_ID, new Atom(1)));
if (ret.isDeftype()) {
Var.pushThreadBindings(RT.mapUniqueKeys(METHOD, null, LOCAL_ENV,
ret.fields, COMPILE_STUB_SYM, Symbol.intern(null, tagName),
COMPILE_STUB_CLASS, stub));
ret.hintedFields = RT.subvec(fieldSyms, 0, fieldSyms.count()
- ret.altCtorDrops);
}
// now (methodname [args] body)*
ret.line = lineDeref();
ret.column = columnDeref();
IPersistentCollection methods = null;
for (ISeq s = methodForms; s != null; s = RT.next(s)) {
NewInstanceMethod m = NewInstanceMethod.parse(ret,
(ISeq) RT.first(s), thistag, overrideables, parameters);
methods = RT.conj(methods, m);
allmethods.remove(m.mk);
}
IPersistentVector overrideMethods = RT.vector();
for (Object ee : allmethods.entrySet()) {
Entry e = (Entry) ee;
java.lang.reflect.Method method = (java.lang.reflect.Method) e
.getValue();
Class> dc = method.getDeclaringClass();
if (!dc.equals(IMeta.class) && !dc.equals(IObj.class)) {
if (Modifier.isAbstract(method.getModifiers())) {
overrideMethods = overrideMethods.cons(method);
}
}
}
ret.overrideMethods = overrideMethods;
ret.methods = methods;
ret.keywords = (IPersistentMap) KEYWORDS.deref();
ret.vars = (IPersistentMap) VARS.deref();
ret.constants = (PersistentVector) CONSTANTS.deref();
ret.constantsID = RT.nextID();
ret.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
ret.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
ret.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
} finally {
if (ret.isDeftype())
Var.popThreadBindings();
Var.popThreadBindings();
}
try {
ret.compile(slashname(superClass), inames, false);
} catch (IOException e) {
throw Util.sneakyThrow(e);
}
ret.getCompiledClass();
return ret;
}
/***
* Current host interop uses reflection, which requires pre-existing classes
* Work around this by: Generate a stub class that has the same interfaces
* and fields as the class we are generating. Use it as a type hint for
* this, and bind the simple name of the class to this stub (in resolve etc)
* Unmunge the name (using a magic prefix) on any code gen for classes
*/
static Class compileStub(String superName, NewInstanceExpr ret,
String[] interfaceNames, Object frm) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = cw;
Var.pushThreadBindings(RT.mapUniqueKeys(SOURCE_WRITER, cw.getSc()));
cv.visit(V1_5, ACC_PUBLIC + ACC_SUPER, COMPILE_STUB_PREFIX + "/"
+ ret.internalName, null, superName, interfaceNames);
int lastSlash = ret.internalName.lastIndexOf('/');
String className;
String packageName;
if (lastSlash != -1) {
className = ret.internalName.substring(lastSlash + 1);
packageName = ret.internalName.substring(0, lastSlash).replaceAll("/",
".");
} else {
packageName = "";
className = ret.internalName;
}
packageName = COMPILE_STUB_PREFIX + "." + packageName;
emitSource("package " + packageName + ";");
emitSource();
emitSource("import clojure.lang.*;");
emitSource();
StringBuilder sb = new StringBuilder();
for (String i : interfaceNames) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(i.replaceAll("/", "."));
}
emitSource("@com.google.j2objc.annotations.ReflectionSupport(com.google.j2objc.annotations.ReflectionSupport.Level.NATIVE_ONLY)");
emitSource("public class " + className + " extends "
+ superName.replaceAll("/", ".")
+ (sb.length() > 0 ? " implements " + sb.toString() : "") + " {");
tab();
// instance fields for closed-overs
for (ISeq s = RT.keys(ret.closes); s != null; s = s.next()) {
LocalBinding lb = (LocalBinding) s.first();
int access = ACC_PUBLIC
+ (ret.isVolatile(lb) ? ACC_VOLATILE : ret.isMutable(lb) ? 0
: ACC_FINAL);
String modif = ret.isVolatile(lb) ? "volatile "
: ret.isMutable(lb) ? "" : "final ";
if (lb.getPrimitiveType() != null) {
cv.visitField(access, lb.name, Type.getType(lb.getPrimitiveType())
.getDescriptor(), null, null);
emitSource(modif + printClass(lb.getPrimitiveType()) + " "
+ lb.print() + ";");
} else {
// todo - when closed-overs are fields, use more specific types here
// and in ctor and emitLocal?
cv.visitField(access, lb.name, OBJECT_TYPE.getDescriptor(), null,
null);
emitSource(modif + "Object " + lb.print() + ";");
}
}
// ctor that takes closed-overs and does nothing
Method m = new Method("", Type.VOID_TYPE, ret.ctorTypes());
GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC, m, null,
null, cv);
StringBuilder cparams = new StringBuilder();
StringBuilder sparams = new StringBuilder();
int n = 0;
for (Type t : ret.ctorTypes()) {
if (cparams.length() > 0) {
cparams.append(", ");
sparams.append(", ");
}
cparams.append(printClass(t) + " p" + n);
sparams.append("p" + n);
n++;
}
emitSource("public " + className + "(" + cparams + ") {");
tab();
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);
emitSource("super(" + sparams + ");");
ctorgen.returnValue();
ctorgen.endMethod();
untab();
emitSource("}");
if (ret.altCtorDrops > 0) {
Type[] ctorTypes = ret.ctorTypes();
Type[] altCtorTypes = new Type[ctorTypes.length - ret.altCtorDrops];
for (int i = 0; i < altCtorTypes.length; i++)
altCtorTypes[i] = ctorTypes[i];
Method alt = new Method("", Type.VOID_TYPE, altCtorTypes);
cparams = new StringBuilder();
sparams = new StringBuilder();
n = 0;
for (Type t : altCtorTypes) {
if (cparams.length() > 0) {
cparams.append(", ");
sparams.append(", ");
}
cparams.append(printClass(t) + " p" + n);
sparams.append("p" + n);
n++;
}
emitSource("public " + className + "(" + cparams + ") {");
tab();
emitSource("super(" + sparams + ");");
untab();
emitSource("}");
ctorgen = new GeneratorAdapter(ACC_PUBLIC, alt, null, null, cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
for (int i = 0; i < ret.altCtorDrops; i++)
ctorgen.visitInsn(Opcodes.ACONST_NULL);
ctorgen.invokeConstructor(
Type.getObjectType(COMPILE_STUB_PREFIX + "/" + ret.internalName),
new Method("", Type.VOID_TYPE, ctorTypes));
ctorgen.returnValue();
ctorgen.endMethod();
}
// end of class
cv.visitEnd();
byte[] bytecode = cw.toByteArray();
DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref();
untab();
emitSource("}");
// TODO?
// if (RT.booleanCast(COMPILE_FILES.deref())) {
// try {
// writeSourceFile(COMPILE_STUB_PREFIX + "/" + ret.internalName,
// SOURCE_WRITER.deref().toString());
// } catch (IOException e) {
// throw Util.sneakyThrow(e);
// }
// }
Var.popThreadBindings();
return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode,
frm);
}
static String[] interfaceNames(IPersistentVector interfaces) {
int icnt = interfaces.count();
String[] inames = icnt > 0 ? new String[icnt] : null;
for (int i = 0; i < icnt; i++)
inames[i] = slashname((Class) interfaces.nth(i));
return inames;
}
static String slashname(Class c) {
return c.getName().replace('.', '/');
}
protected void emitStatics(ClassVisitor cv) {
if (this.isDeftype()) {
// getBasis()
Method meth = Method
.getMethod("clojure.lang.IPersistentVector getBasis()");
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
meth, null, null, cv);
emitSource("public static clojure.lang.IPersistentVector getBasis() {");
tab();
emitSource("return " + emitValue(hintedFields, gen) + ";");
untab();
emitSource("}");
gen.returnValue();
gen.endMethod();
if (this.fields.count() > this.hintedFields.count()) {
// create(IPersistentMap)
String className = name.replace('.', '/');
int i = 1;
int fieldCount = hintedFields.count();
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "create",
"(Lclojure/lang/IPersistentMap;)L" + className + ";", null, null);
mv.visitCode();
emitSource("public static " + printClass(name)
+ " create(IPersistentMap map) {");
tab();
StringBuilder sb = new StringBuilder();
StringBuilder others = new StringBuilder();
for (ISeq s = RT.seq(hintedFields); s != null; s = s.next(), i++) {
if (sb.length() > 0) {
sb.append(", ");
}
String bName = ((Symbol) s.first()).name;
Class k = tagClass(tagOf(s.first()));
others.append(".without(Keyword.intern(\"" + bName + "\"))");
String val = "map.valAt(Keyword.intern(\"" + bName + "\"), null)";
if (k.isPrimitive()) {
sb.append("(" + printClass(boxClass(k)) + ")" + val);
} else {
sb.append(val);
}
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(bName);
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/Keyword", "intern",
"(Ljava/lang/String;)Lclojure/lang/Keyword;");
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKEINTERFACE, "clojure/lang/IPersistentMap",
"valAt",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
if (k.isPrimitive()) {
mv.visitTypeInsn(CHECKCAST, Type.getType(boxClass(k))
.getInternalName());
}
mv.visitVarInsn(ASTORE, i);
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(bName);
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/Keyword", "intern",
"(Ljava/lang/String;)Lclojure/lang/Keyword;");
mv.visitMethodInsn(INVOKEINTERFACE, "clojure/lang/IPersistentMap",
"without", "(Ljava/lang/Object;)Lclojure/lang/IPersistentMap;");
mv.visitVarInsn(ASTORE, 0);
}
mv.visitTypeInsn(Opcodes.NEW, className);
mv.visitInsn(DUP);
Method ctor = new Method("", Type.VOID_TYPE, ctorTypes());
if (hintedFields.count() > 0) {
for (i = 1; i <= fieldCount; i++) {
mv.visitVarInsn(ALOAD, i);
Class k = tagClass(tagOf(hintedFields.nth(i - 1)));
if (k.isPrimitive()) {
String b = Type.getType(boxClass(k)).getInternalName();
String p = Type.getType(k).getDescriptor();
String n = k.getName();
mv.visitMethodInsn(INVOKEVIRTUAL, b, n + "Value", "()" + p);
}
}
}
emitSource("return new " + printClass(name) + "(" + sb
+ (sb.length() > 0 ? ", " : "") + "null, RT.seqOrElse(map"
+ others + "));");
mv.visitInsn(ACONST_NULL);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/RT", "seqOrElse",
"(Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitMethodInsn(INVOKESPECIAL, className, "",
ctor.getDescriptor());
mv.visitInsn(ARETURN);
mv.visitMaxs(4 + fieldCount, 1 + fieldCount);
mv.visitEnd();
untab();
emitSource("}");
}
}
}
protected void emitMethods(ClassVisitor cv) {
for (ISeq s = RT.seq(methods); s != null; s = s.next()) {
ObjMethod method = (ObjMethod) s.first();
method.emit(this, cv);
}
for (ISeq i = overrideMethods.seq(); i != null; i = i.next()) {
java.lang.reflect.Method m = (java.lang.reflect.Method) i.first();
StringBuilder sb = new StringBuilder();
int n = 0;
for (Class> p : m.getParameterTypes()) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(printClass(p) + " p" + n);
n++;
}
StringBuilder exs = new StringBuilder();
for (Class> e : m.getExceptionTypes()) {
if (exs.length() > 0) {
exs.append(", ");
}
exs.append(printClass(e));
}
emitSource("public " + printClass(m.getReturnType()) + " "
+ m.getName() + "(" + sb + ") "
+ (exs.length() > 0 ? "throws " : "") + exs + " {");
tab();
emitSource("throw new RuntimeException(\"Reify non implemented method\");");
untab();
emitSource("}");
}
// emit bridge methods
for (Map.Entry> e : covariants.entrySet()) {
java.lang.reflect.Method m = mmap.get(e.getKey());
Class[] params = m.getParameterTypes();
Type[] argTypes = new Type[params.length];
for (int i = 0; i < params.length; i++) {
argTypes[i] = Type.getType(params[i]);
}
Method target = new Method(m.getName(),
Type.getType(m.getReturnType()), argTypes);
for (Class retType : e.getValue()) {
Method meth = new Method(m.getName(), Type.getType(retType), argTypes);
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_BRIDGE,
meth, null,
// todo don't hardwire this
EXCEPTION_TYPES, cv);
gen.visitCode();
gen.loadThis();
gen.loadArgs();
gen.invokeInterface(Type.getType(m.getDeclaringClass()), target);
gen.returnValue();
gen.endMethod();
}
}
}
static public IPersistentVector msig(java.lang.reflect.Method m) {
return RT.vector(m.getName(), RT.seq(m.getParameterTypes()),
m.getReturnType());
}
static void considerMethod(java.lang.reflect.Method m, Map mm) {
IPersistentVector mk = msig(m);
int mods = m.getModifiers();
if (!(mm.containsKey(mk)
|| !(Modifier.isPublic(mods) || Modifier.isProtected(mods))
|| Modifier.isStatic(mods) || Modifier.isFinal(mods))) {
mm.put(mk, m);
}
}
static void gatherMethods(Class c, Map mm) {
for (; c != null; c = c.getSuperclass()) {
for (java.lang.reflect.Method m : c.getDeclaredMethods())
considerMethod(m, mm);
for (java.lang.reflect.Method m : c.getMethods())
considerMethod(m, mm);
}
}
static public Map[] gatherMethods(Class sc, ISeq interfaces) {
Map> parameters = new HashMap>();
Map allm = new HashMap();
gatherMethods(sc, allm);
for (; interfaces != null; interfaces = interfaces.next()) {
Class i = (Class) interfaces.first();
gatherMethods(i, allm);
for (java.lang.reflect.Type t : i.getGenericInterfaces()) {
if (t instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) t;
Class r = (Class) p.getRawType();
TypeVariable[] l = r.getTypeParameters();
java.lang.reflect.Type[] k = p.getActualTypeArguments();
if (l.length != k.length) {
throw new RuntimeException();
}
HashMap params = new HashMap();
for (int j = 0; j < k.length; j++) {
if (k[j] instanceof Class) {
params.put(l[j].toString(), (Class) k[j]);
}
}
if (!params.isEmpty()) {
parameters.put((Class) ((ParameterizedType) t).getRawType(),
params);
}
}
}
}
Map mm = new HashMap();
Map> covariants = new HashMap>();
for (Object o : allm.entrySet()) {
Map.Entry e = (Map.Entry) o;
IPersistentVector mk = (IPersistentVector) e.getKey();
mk = (IPersistentVector) mk.pop();
java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();
if (mm.containsKey(mk)) // covariant return
{
Set cvs = covariants.get(mk);
if (cvs == null) {
cvs = new HashSet();
covariants.put(mk, cvs);
}
java.lang.reflect.Method om = mm.get(mk);
if (om.getReturnType().isAssignableFrom(m.getReturnType())) {
cvs.add(om.getReturnType());
mm.put(mk, m);
} else
cvs.add(m.getReturnType());
} else
mm.put(mk, m);
}
return new Map[] { mm, covariants, parameters };
}
}
public static class NewInstanceMethod extends ObjMethod {
String name;
Type[] argTypes;
Type retType;
Class retClass;
Class[] exclasses;
static Symbol dummyThis = Symbol.intern(null, "dummy_this_dlskjsdfower");
private IPersistentVector parms;
private Symbol thisName;
private Object mk;
public NewInstanceMethod(ObjExpr objx, ObjMethod parent) {
super(objx, parent);
}
int numParams() {
return argLocals.count();
}
String getMethodName() {
return name;
}
Type getReturnType() {
return retType;
}
Type[] getArgTypes() {
return argTypes;
}
static public IPersistentVector msig(String name,
java.lang.reflect.Type[] paramTypes) {
return RT.vector(name, RT.seq(paramTypes));
}
static NewInstanceMethod parse(ObjExpr objx, ISeq form, Symbol thistag,
Map overrideables, Map parameters) {
// (methodname [this-name args*] body...)
// this-name might be nil
NewInstanceMethod method = new NewInstanceMethod(objx,
(ObjMethod) METHOD.deref());
Symbol dotname = (Symbol) RT.first(form);
Symbol name = (Symbol) Symbol.intern(null, munge(dotname.name)).withMeta(
RT.meta(dotname));
IPersistentVector parms = (IPersistentVector) RT.second(form);
if (parms.count() == 0) {
throw new IllegalArgumentException(
"Must supply at least one argument for 'this' in: " + dotname);
}
Symbol thisName = (Symbol) parms.nth(0);
parms = RT.subvec(parms, 1, parms.count());
ISeq body = RT.next(RT.next(form));
try {
method.line = lineDeref();
method.column = columnDeref();
// register as the current method and set up a new env frame
PathNode pnode = new PathNode(PATHTYPE.PATH,
(PathNode) CLEAR_PATH.get());
Var.pushThreadBindings(RT.mapUniqueKeys(METHOD, method, LOCAL_ENV,
LOCAL_ENV.deref(), LOOP_LOCALS, null, NEXT_LOCAL_NUM, 0,
CLEAR_PATH, pnode, CLEAR_ROOT, pnode, CLEAR_SITES,
PersistentHashMap.EMPTY));
// register 'this' as local 0
if (thisName != null)
registerLocal((thisName == null) ? dummyThis : thisName, thistag,
null, false);
else
getAndIncLocalNum();
PersistentVector argLocals = PersistentVector.EMPTY;
method.thisName = thisName;
method.retClass = tagClass(tagOf(name));
method.argTypes = new Type[parms.count()];
boolean hinted = tagOf(name) != null;
Class[] pclasses = new Class[parms.count()];
java.lang.reflect.Type[] gclasses = new java.lang.reflect.Type[parms
.count()];
Symbol[] psyms = new Symbol[parms.count()];
for (int i = 0; i < parms.count(); i++) {
if (!(parms.nth(i) instanceof Symbol))
throw new IllegalArgumentException("params must be Symbols");
Symbol p = (Symbol) parms.nth(i);
Object tag = tagOf(p);
if (tag != null)
hinted = true;
if (p.getNamespace() != null)
p = Symbol.intern(p.name);
Class pclass = tagClass(tag);
pclasses[i] = pclass;
psyms[i] = p;
}
Map matches = findMethodsWithNameAndArity(name.name, parms.count(),
overrideables);
Object mk = msig(name.name, pclasses);
java.lang.reflect.Method m = null;
if (matches.size() > 0) {
// multiple methods
if (matches.size() > 1) {
// must be hinted and match one method
if (!hinted)
throw new IllegalArgumentException(
"Must hint overloaded method: " + name.name);
m = (java.lang.reflect.Method) matches.get(mk);
if (m == null)
throw new IllegalArgumentException(
"Can't find matching overloaded method: " + name.name);
if (m.getReturnType() != method.retClass)
throw new IllegalArgumentException("Mismatched return type: "
+ name.name + ", expected: " + m.getReturnType().getName()
+ ", had: " + method.retClass.getName());
} else // one match
{
// if hinted, validate match,
if (hinted) {
m = (java.lang.reflect.Method) matches.get(mk);
if (m == null)
throw new IllegalArgumentException(
"Can't find matching method: " + name.name
+ ", leave off hints for auto match.");
if (m.getReturnType() != method.retClass)
throw new IllegalArgumentException("Mismatched return type: "
+ name.name + ", expected: " + m.getReturnType().getName()
+ ", had: " + method.retClass.getName());
} else // adopt found method sig
{
m = (java.lang.reflect.Method) matches.values().iterator().next();
method.retClass = m.getReturnType();
pclasses = m.getParameterTypes();
gclasses = m.getGenericParameterTypes();
}
}
}
// else if(findMethodsWithName(name.name,allmethods).size()>0)
// throw new IllegalArgumentException("Can't override/overload method: "
// + name.name);
else
throw new IllegalArgumentException(
"Can't define method not in interfaces: " + name.name);
// else
// validate unque name+arity among additional methods
IPersistentVector types = RT.vector();
for (Class> t : m.getParameterTypes()) {
types = types.cons(t);
}
method.mk = RT.vector(m.getName(), types.seq());
method.retType = Type.getType(method.retClass);
method.exclasses = m.getExceptionTypes();
for (int i = 0; i < parms.count(); i++) {
java.lang.reflect.Type c = pclasses[i];
if (!parameters.isEmpty() && gclasses[i] != null) {
if (gclasses[i] instanceof TypeVariable) {
Map pm = (Map) parameters.get(m.getDeclaringClass());
String key = gclasses[i].toString();
if (pm != null && pm.containsKey(key)) {
c = (java.lang.reflect.Type) pm.get(key);
}
}
}
LocalBinding lb = registerLocal(psyms[i], null, new MethodParamExpr(
(Class) c), true);
argLocals = argLocals.assocN(i, lb);
method.argTypes[i] = Type.getType((Class) c);
}
for (int i = 0; i < parms.count(); i++) {
if (pclasses[i] == long.class || pclasses[i] == double.class)
getAndIncLocalNum();
}
LOOP_LOCALS.set(argLocals);
method.name = name.name;
method.methodMeta = RT.meta(name);
method.parms = parms;
method.argLocals = argLocals;
method.body = (new BodyExpr.Parser()).parse(C.RETURN, body);
return method;
} finally {
Var.popThreadBindings();
}
}
private static Map findMethodsWithNameAndArity(String name, int arity,
Map mm) {
Map ret = new HashMap();
for (Object o : mm.entrySet()) {
Map.Entry e = (Map.Entry) o;
java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();
if (name.equals(m.getName()) && m.getParameterTypes().length == arity)
ret.put(e.getKey(), e.getValue());
}
return ret;
}
// private static Map findMethodsWithName(String name, Map mm) {
// Map ret = new HashMap();
// for (Object o : mm.entrySet()) {
// Map.Entry e = (Map.Entry) o;
// java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();
// if (name.equals(m.getName()))
// ret.put(e.getKey(), e.getValue());
// }
// return ret;
// }
public void emit(ObjExpr obj, ClassVisitor cv) {
Method m = new Method(getMethodName(), getReturnType(), getArgTypes());
StringBuilder params = new StringBuilder();
int pos = 0;
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
if (params.length() > 0) {
params.append(", ");
}
params.append(printClass(getArgTypes()[pos]) + " " + lb.print());
pos++;
}
Type[] extypes = null;
StringBuilder exs = null;
if (exclasses.length > 0) {
exs = new StringBuilder();
extypes = new Type[exclasses.length];
for (int i = 0; i < exclasses.length; i++) {
if (exs.length() > 0) {
exs.append(", ");
}
extypes[i] = Type.getType(exclasses[i]);
exs.append(printClass(exclasses[i]));
}
}
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC, m, null, extypes,
cv);
addAnnotation(gen, methodMeta);
emitSource("public " + printClass(getReturnType()) + " "
+ getMethodName() + "(" + params + ") "
+ (exclasses.length > 0 ? "throws " + exs.toString() : "") + " {");
tab();
if (hasException) {
emitSource("try {");
tab();
}
if (hasRecur) {
emitSource("while(true) {");
tab();
}
for (int i = 0; i < parms.count(); i++) {
IPersistentMap meta = RT.meta(parms.nth(i));
addParameterAnnotation(gen, meta, i);
}
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try {
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
String b = emitBody(objx, gen, retClass, body);
if (b != null) {
emitSource(b);
}
Label end = gen.mark();
gen.visitLocalVariable("this", obj.objtype.getDescriptor(), null,
loopLabel, end, 0);
for (ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next()) {
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, argTypes[lb.idx - 1].getDescriptor(),
null, loopLabel, end, lb.idx);
}
} finally {
Var.popThreadBindings();
}
gen.returnValue();
// gen.visitMaxs(1, 1);
gen.endMethod();
if (hasRecur) {
untab();
emitSource("}");
}
if (hasException) {
untab();
emitSource("} catch (Exception ___e) {");
tab();
emitSource("throw Util.sneakyThrow(___e);");
untab();
emitSource("}");
}
untab();
emitSource("}");
}
}
static Class primClass(Symbol sym) {
if (sym == null)
return null;
Class c = null;
if (sym.name.equals("int"))
c = int.class;
else if (sym.name.equals("long"))
c = long.class;
else if (sym.name.equals("float"))
c = float.class;
else if (sym.name.equals("double"))
c = double.class;
else if (sym.name.equals("char"))
c = char.class;
else if (sym.name.equals("short"))
c = short.class;
else if (sym.name.equals("byte"))
c = byte.class;
else if (sym.name.equals("boolean"))
c = boolean.class;
else if (sym.name.equals("void"))
c = void.class;
return c;
}
static Class tagClass(Object tag) {
if (tag == null)
return Object.class;
Class c = null;
if (tag instanceof Symbol)
c = primClass((Symbol) tag);
if (c == null)
c = HostExpr.tagToClass(tag);
return c;
}
static Class primClass(Class c) {
return c.isPrimitive() ? c : Object.class;
}
static Class boxClass(Class p) {
if (!p.isPrimitive())
return p;
Class c = null;
if (p == Integer.TYPE)
c = Integer.class;
else if (p == Long.TYPE)
c = Long.class;
else if (p == Float.TYPE)
c = Float.class;
else if (p == Double.TYPE)
c = Double.class;
else if (p == Character.TYPE)
c = Character.class;
else if (p == Short.TYPE)
c = Short.class;
else if (p == Byte.TYPE)
c = Byte.class;
else if (p == Boolean.TYPE)
c = Boolean.class;
return c;
}
static public class MethodParamExpr implements Expr, MaybePrimitiveExpr {
final Class c;
public MethodParamExpr(Class c) {
this.c = c;
}
public Object eval() {
throw Util.runtimeException("Can't eval");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
throw Util.runtimeException("Can't emit");
}
public boolean hasJavaClass() {
return c != null;
}
public Class getJavaClass() {
return c;
}
public boolean canEmitPrimitive() {
return Util.isPrimitive(c);
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
throw Util.runtimeException("Can't emit");
}
}
public static class CaseExpr implements Expr, MaybePrimitiveExpr {
public final LocalBindingExpr expr;
public final int shift, mask, low, high;
public final Expr defaultExpr;
public final SortedMap tests;
public final HashMap thens;
public final Keyword switchType;
public final Keyword testType;
public final Set skipCheck;
public final Class returnType;
public final int line;
public final int column;
final static Type NUMBER_TYPE = Type.getType(Number.class);
final static Method intValueMethod = Method.getMethod("int intValue()");
final static Method hashMethod = Method.getMethod("int hash(Object)");
final static Method hashCodeMethod = Method.getMethod("int hashCode()");
final static Method equivMethod = Method
.getMethod("boolean equiv(Object, Object)");
final static Keyword compactKey = Keyword.intern(null, "compact");
final static Keyword sparseKey = Keyword.intern(null, "sparse");
final static Keyword hashIdentityKey = Keyword
.intern(null, "hash-identity");
final static Keyword hashEquivKey = Keyword.intern(null, "hash-equiv");
final static Keyword intKey = Keyword.intern(null, "int");
// (case* expr shift mask default map table-type
// test-type skip-check?)
public CaseExpr(int line, int column, LocalBindingExpr expr, int shift,
int mask, int low, int high, Expr defaultExpr,
SortedMap tests, HashMap thens,
Keyword switchType, Keyword testType, Set skipCheck) {
this.expr = expr;
this.shift = shift;
this.mask = mask;
this.low = low;
this.high = high;
this.defaultExpr = defaultExpr;
this.tests = tests;
this.thens = thens;
this.line = line;
this.column = column;
if (switchType != compactKey && switchType != sparseKey)
throw new IllegalArgumentException("Unexpected switch type: "
+ switchType);
this.switchType = switchType;
if (testType != intKey && testType != hashEquivKey
&& testType != hashIdentityKey)
throw new IllegalArgumentException("Unexpected test type: "
+ switchType);
this.testType = testType;
this.skipCheck = skipCheck;
Collection returns = new ArrayList(thens.values());
returns.add(defaultExpr);
this.returnType = maybeJavaClass(returns);
if (RT.count(skipCheck) > 0
&& RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) {
RT.errPrintWriter()
.format(
"Performance warning, %s:%d:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n",
SOURCE_PATH.deref(), line, column);
}
}
public boolean hasJavaClass() {
return returnType != null;
}
public boolean canEmitPrimitive() {
return Util.isPrimitive(returnType);
}
public Class getJavaClass() {
return returnType;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval case");
}
public String emit(C context, ObjExpr objx, GeneratorAdapter gen) {
return doEmit(context, objx, gen, false);
}
public String emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
return doEmit(context, objx, gen, true);
}
public String doEmit(C context, ObjExpr objx, GeneratorAdapter gen,
boolean emitUnboxed) {
String r = registerTemp();
Label defaultLabel = gen.newLabel();
Label endLabel = gen.newLabel();
SortedMap labels = new TreeMap();
if (context == C.EXPRESSION) {
emitSource("Object " + r + " = null;");
}
for (Integer i : tests.keySet()) {
labels.put(i, gen.newLabel());
}
gen.visitLineNumber(line, gen.mark());
Class primExprClass = maybePrimitiveType(expr);
Type primExprType = primExprClass == null ? null : Type
.getType(primExprClass);
String cond;
if (testType == intKey)
cond = emitExprForInts(objx, gen, primExprType, defaultLabel);
else
cond = emitExprForHashes(objx, gen);
emitSource("switch (" + cond + ") {");
tab();
if (switchType == sparseKey) {
Label[] la = new Label[labels.size()];
la = labels.values().toArray(la);
int[] ints = Numbers.int_array(tests.keySet());
gen.visitLookupSwitchInsn(defaultLabel, ints, la);
} else {
Label[] la = new Label[(high - low) + 1];
for (int i = low; i <= high; i++) {
la[i - low] = labels.containsKey(i) ? labels.get(i) : defaultLabel;
}
gen.visitTableSwitchInsn(low, high, defaultLabel, la);
}
int pos = 0;
for (Integer i : labels.keySet()) {
emitSource("case " + i + ":");
pos++;
tab();
gen.mark(labels.get(i));
if (testType == intKey) {
emitThenForInts(context, r, objx, gen, primExprType, tests.get(i),
thens.get(i), defaultLabel, emitUnboxed);
} else if (RT.contains(skipCheck, i) == RT.T) {
String e = emitExpr(context, objx, gen, thens.get(i), emitUnboxed);
if (e != null) {
if (context != C.STATEMENT) {
emitAssigRet(context, r, e);
}
}
if (context != C.RETURN) {
emitSource("break;");
}
} else {
emitThenForHashes(context, r, objx, gen, tests.get(i), thens.get(i),
defaultLabel, emitUnboxed);
}
gen.goTo(endLabel);
untab();
}
gen.mark(defaultLabel);
emitSource("default:");
tab();
String e = emitExpr(context, objx, gen, defaultExpr, emitUnboxed);
if (e != null) {
if (context != C.STATEMENT) {
emitAssigRet(context, r, e);
}
}
untab();
untab();
emitSource("}");
gen.mark(endLabel);
return context == C.EXPRESSION ? r : "";
}
private boolean isShiftMasked() {
return mask != 0;
}
private String emitShiftMask(GeneratorAdapter gen, String v) {
if (isShiftMasked()) {
gen.push(shift);
gen.visitInsn(ISHR);
gen.push(mask);
gen.visitInsn(IAND);
return v + " >> " + shift + " & " + mask;
}
return v;
}
private String emitExprForInts(ObjExpr objx, GeneratorAdapter gen,
Type exprType, Label defaultLabel) {
if (exprType == null) {
if (RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) {
RT.errPrintWriter()
.format(
"Performance warning, %s:%d:%d - case has int tests, but tested expression is not primitive.\n",
SOURCE_PATH.deref(), line, column);
}
String val = expr.emit(C.EXPRESSION, objx, gen);
gen.instanceOf(NUMBER_TYPE);
gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
expr.emit(C.EXPRESSION, objx, gen);
gen.checkCast(NUMBER_TYPE);
gen.invokeVirtual(NUMBER_TYPE, intValueMethod);
return emitShiftMask(gen, "RT.uncheckedIntCast(" + val + ")");
} else if (exprType == Type.LONG_TYPE || exprType == Type.INT_TYPE
|| exprType == Type.SHORT_TYPE || exprType == Type.BYTE_TYPE) {
String val = expr.emitUnboxed(C.EXPRESSION, objx, gen);
gen.cast(exprType, Type.INT_TYPE);
return emitShiftMask(gen, "(int)" + val);
} else {
gen.goTo(defaultLabel);
return "-666123"; // Magic number to jump to default TODO proper impl
}
}
private void emitThenForInts(C context, String r, ObjExpr objx,
GeneratorAdapter gen, Type exprType, Expr test, Expr then,
Label defaultLabel, boolean emitUnboxed) {
if (exprType == null) {
String val = expr.emit(C.EXPRESSION, objx, gen);
String tval = test.emit(C.EXPRESSION, objx, gen);
gen.invokeStatic(UTIL_TYPE, equivMethod);
gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
emitSource("if (Util.equiv(" + val + ", " + tval + ")) {");
tab();
String body = emitExpr(context, objx, gen, then, emitUnboxed);
if (body != null) {
if (context != C.STATEMENT) {
emitAssigRet(context, r, body);
}
}
if (context != C.RETURN) {
emitSource("break;");
}
untab();
emitSource("}");
} else if (exprType == Type.LONG_TYPE) {
String val = ((NumberExpr) test).emitUnboxed(C.EXPRESSION, objx, gen);
String tval = expr.emitUnboxed(C.EXPRESSION, objx, gen);
gen.ifCmp(Type.LONG_TYPE, GeneratorAdapter.NE, defaultLabel);
emitSource("if (" + val + " == " + tval + ") {");
tab();
String body = emitExpr(context, objx, gen, then, emitUnboxed);
if (body != null) {
if (context != C.STATEMENT) {
emitAssigRet(context, r, body);
}
}
if (context != C.RETURN) {
emitSource("break;");
}
untab();
emitSource("}");
} else if (exprType == Type.INT_TYPE || exprType == Type.SHORT_TYPE
|| exprType == Type.BYTE_TYPE) {
if (isShiftMasked()) {
((NumberExpr) test).emitUnboxed(C.EXPRESSION, objx, gen);
expr.emitUnboxed(C.EXPRESSION, objx, gen);
gen.cast(exprType, Type.LONG_TYPE);
gen.ifCmp(Type.LONG_TYPE, GeneratorAdapter.NE, defaultLabel);
}
String val = ((NumberExpr) test).n.toString();
String tval = expr.b.print();
emitSource("if (" + val + " == " + tval + ") {");
tab();
// else direct match
String body = emitExpr(context, objx, gen, then, emitUnboxed);
if (body != null) {
if (context != C.STATEMENT) {
emitAssigRet(context, r, body);
}
}
if (context != C.RETURN) {
emitSource("break;");
}
untab();
emitSource("}");
} else {
gen.goTo(defaultLabel);
}
}
private String emitExprForHashes(ObjExpr objx, GeneratorAdapter gen) {
String val = expr.emit(C.EXPRESSION, objx, gen);
gen.invokeStatic(UTIL_TYPE, hashMethod);
return emitShiftMask(gen, "Util.hash(" + val + ")");
}
private void emitThenForHashes(C context, String r, ObjExpr objx,
GeneratorAdapter gen, Expr test, Expr then, Label defaultLabel,
boolean emitUnboxed) {
String val = expr.emit(C.EXPRESSION, objx, gen);
String tval = test.emit(C.EXPRESSION, objx, gen);
if (testType == hashIdentityKey) {
gen.visitJumpInsn(IF_ACMPNE, defaultLabel);
emitSource("if (" + val + " == " + tval + ") {");
} else {
emitSource("if (Util.equiv(" + val + ", " + tval + ")) {");
gen.invokeStatic(UTIL_TYPE, equivMethod);
gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
}
tab();
String e = emitExpr(context, objx, gen, then, emitUnboxed);
if (e != null) {
if (context != C.STATEMENT) {
emitAssigRet(context, r, e);
}
}
if (context != C.RETURN) {
emitSource("break;");
}
untab();
emitSource("}");
}
private static String emitExpr(C context, ObjExpr objx, GeneratorAdapter gen,
Expr expr, boolean emitUnboxed) {
if (emitUnboxed && expr instanceof MaybePrimitiveExpr)
return ((MaybePrimitiveExpr) expr).emitUnboxed(context, objx, gen);
else
return expr.emit(context, objx, gen);
}
static class Parser implements IParser {
// (case* expr shift mask default map table-type
// test-type skip-check?)
// prepared by case macro and presumed correct
// case macro binds actual expr in let so expr is always a local,
// no need to worry about multiple evaluation
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
if (context == C.EVAL)
return analyze(context,
RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
IPersistentVector args = LazilyPersistentVector.create(form.next());
Object exprForm = args.nth(0);
int shift = ((Number) args.nth(1)).intValue();
int mask = ((Number) args.nth(2)).intValue();
Object defaultForm = args.nth(3);
Map caseMap = (Map) args.nth(4);
Keyword switchType = ((Keyword) args.nth(5));
Keyword testType = ((Keyword) args.nth(6));
Set skipCheck = RT.count(args) < 8 ? null : (Set) args.nth(7);
ISeq keys = RT.keys(caseMap);
int low = ((Number) RT.first(keys)).intValue();
int high = ((Number) RT.nth(keys, RT.count(keys) - 1)).intValue();
LocalBindingExpr testexpr = (LocalBindingExpr) analyze(C.EXPRESSION,
exprForm);
testexpr.shouldClear = false;
SortedMap tests = new TreeMap();
HashMap thens = new HashMap();
PathNode branch = new PathNode(PATHTYPE.BRANCH,
(PathNode) CLEAR_PATH.get());
for (Object o : caseMap.entrySet()) {
Map.Entry e = (Map.Entry) o;
Integer minhash = ((Number) e.getKey()).intValue();
Object pair = e.getValue(); // [test-val then-expr]
Expr testExpr = testType == intKey ? NumberExpr.parse(((Number) RT
.first(pair)).intValue()) : new ConstantExpr(RT.first(pair));
tests.put(minhash, testExpr);
Expr thenExpr;
try {
Var.pushThreadBindings(RT.map(CLEAR_PATH, new PathNode(
PATHTYPE.PATH, branch)));
thenExpr = analyze(context, RT.second(pair));
} finally {
Var.popThreadBindings();
}
thens.put(minhash, thenExpr);
}
Expr defaultExpr;
try {
Var.pushThreadBindings(RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,
branch)));
defaultExpr = analyze(context, args.nth(3));
} finally {
Var.popThreadBindings();
}
int line = ((Number) LINE.deref()).intValue();
int column = ((Number) COLUMN.deref()).intValue();
return new CaseExpr(line, column, testexpr, shift, mask, low, high,
defaultExpr, tests, thens, switchType, testType, skipCheck);
}
}
}
static IPersistentCollection emptyVarCallSites() {
return PersistentHashSet.EMPTY;
}
private static String wrap(C context, String v) {
if (C.RETURN == context) {
Class retClass = RETURN_TYPE.isBound() ? (Class) RETURN_TYPE.deref()
: Object.class;
if (retClass != void.class) {
v = "return " + unboxVal(retClass, v);
} else {
v = "return";
}
}
return v + (C.EXPRESSION != context ? ";" : "");
}
public static String unboxVal(Class c, String v) {
switch (Type.getType(c).getSort()) {
case Type.CHAR:
v = "RT.charCast(" + v + ")";
break;
case Type.BOOLEAN:
v = "RT.booleanCast(" + v + ")";
break;
case Type.DOUBLE:
v = "RT.doubleCast(" + v + ")";
break;
case Type.FLOAT:
v = "RT.floatCast(" + v + ")";
break;
case Type.LONG:
v = "RT.longCast(" + v + ")";
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
if (v.startsWith("((short)") || v.startsWith("((byte)")
|| v.startsWith("((float)") || v.startsWith("((double)")
|| v.startsWith("((long)")) {
v = "((int)" + v + ")";
} else if (!v.startsWith("((int)")) {
v = "RT.intCast(" + v + ")";
}
break;
default:
if (c != Object.class && c != void.class) {
v = "(" + printClass(c) + ")" + v + "";
}
break;
}
return v;
}
private static void emitAssigRet(C context, String r, String body) {
boolean ex = context == C.EXPRESSION;
if (body.equals("continue;")) {
emitSource(body);
} else {
emitSource((ex ? r + " = " : "") + body + (ex ? ";" : ""));
}
}
}
================================================
FILE: src/jvm/clojure/lang/Cons.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 11:01:29 AM */
package clojure.lang;
import java.io.Serializable;
final public class Cons extends ASeq implements Serializable {
private final Object _first;
private final ISeq _more;
public Cons(Object first, ISeq _more){
this._first = first;
this._more = _more;
}
public Cons(IPersistentMap meta, Object _first, ISeq _more){
super(meta);
this._first = _first;
this._more = _more;
}
public Object first(){
return _first;
}
public ISeq next(){
return more().seq();
}
public ISeq more(){
if(_more == null)
return PersistentList.EMPTY;
return _more;
}
public int count(){
return 1 + RT.count(_more);
}
public Cons withMeta(IPersistentMap meta){
return new Cons(meta, _first, _more);
}
}
================================================
FILE: src/jvm/clojure/lang/Counted.java
================================================
package clojure.lang;
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
/* A class that implements Counted promises that it is a collection
* that implement a constant-time count() */
public interface Counted {
int count();
}
================================================
FILE: src/jvm/clojure/lang/Cycle.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
/* Alex Miller, Dec 5, 2014 */
public class Cycle extends ASeq implements IReduce, IPending {
private final ISeq all; // never null
private final ISeq prev;
private volatile ISeq _current; // lazily realized
private volatile ISeq _next; // cached
private Cycle(ISeq all, ISeq prev, ISeq current){
this.all = all;
this.prev = prev;
this._current = current;
}
private Cycle(IPersistentMap meta, ISeq all, ISeq prev, ISeq current, ISeq next){
super(meta);
this.all = all;
this.prev = prev;
this._current = current;
this._next = next;
}
public static ISeq create(ISeq vals){
if(vals == null)
return PersistentList.EMPTY;
return new Cycle(vals, null, vals);
}
// realization for use of current
private ISeq current() {
if(_current == null) {
ISeq current = prev.next();
_current = (current == null) ? all : current;
}
return _current;
}
public boolean isRealized() {
return _current != null;
}
public Object first(){
return current().first();
}
public ISeq next(){
if(_next == null)
_next = new Cycle(all, current(), null);
return _next;
}
public Cycle withMeta(IPersistentMap meta){
return new Cycle(meta, all, prev, _current, _next);
}
public Object reduce(IFn f){
ISeq s = current();
Object ret = s.first();
while(true) {
s = s.next();
if(s == null)
s = all;
ret = f.invoke(ret, s.first());
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
}
public Object reduce(IFn f, Object start){
Object ret = start;
ISeq s = current();
while(true){
ret = f.invoke(ret, s.first());
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
s = s.next();
if(s == null)
s = all;
}
}
}
================================================
FILE: src/jvm/clojure/lang/Delay.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jun 28, 2007 */
package clojure.lang;
public class Delay implements IDeref, IPending{
Object val;
Throwable exception;
IFn fn;
public Delay(IFn fn){
this.fn = fn;
this.val = null;
this.exception = null;
}
static public Object force(Object x) {
return (x instanceof Delay) ?
((Delay) x).deref()
: x;
}
synchronized public Object deref() {
if(fn != null)
{
try
{
val = fn.invoke();
}
catch(Throwable t)
{
exception = t;
}
fn = null;
}
if(exception != null)
throw Util.sneakyThrow(exception);
return val;
}
synchronized public boolean isRealized(){
return fn == null;
}
}
================================================
FILE: src/jvm/clojure/lang/DynamicClassLoader.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Aug 21, 2007 */
package clojure.lang;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
public class DynamicClassLoader extends URLClassLoader{
HashMap constantVals = new HashMap();
static ConcurrentHashMap>classCache =
new ConcurrentHashMap >();
static final URL[] EMPTY_URLS = new URL[]{};
static final ReferenceQueue rq = new ReferenceQueue();
public DynamicClassLoader(){
//pseudo test in lieu of hasContextClassLoader()
super(EMPTY_URLS,(Thread.currentThread().getContextClassLoader() == null ||
Thread.currentThread().getContextClassLoader() == ClassLoader.getSystemClassLoader())?
Compiler.class.getClassLoader():Thread.currentThread().getContextClassLoader());
}
public DynamicClassLoader(ClassLoader parent){
super(EMPTY_URLS,parent);
}
public Class defineClass(String name, byte[] bytes, Object srcForm){
Util.clearCache(rq, classCache);
Class c = defineClass(name, bytes, 0, bytes.length);
classCache.put(name, new SoftReference(c,rq));
return c;
}
static Class> findInMemoryClass(String name) {
Reference cr = classCache.get(name);
if(cr != null)
{
Class c = cr.get();
if(c != null)
return c;
else
classCache.remove(name, cr);
}
return null;
}
protected Class>findClass(String name) throws ClassNotFoundException {
Class c = findInMemoryClass(name);
if (c != null)
return c;
else
return super.findClass(name);
}
protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = findLoadedClass(name);
if (c == null) {
c = findInMemoryClass(name);
if (c == null)
c = super.loadClass(name, false);
}
if (resolve)
resolveClass(c);
return c;
}
public void registerConstants(int id, Object[] val){
constantVals.put(id, val);
}
public Object[] getConstants(int id){
return constantVals.get(id);
}
public void addURL(URL url){
super.addURL(url);
}
}
================================================
FILE: src/jvm/clojure/lang/EdnReader.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EdnReader{
static IFn[] macros = new IFn[256];
static IFn[] dispatchMacros = new IFn[256];
static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
static Pattern intPat =
Pattern.compile(
"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
static IFn taggedReader = new TaggedReader();
static
{
macros['"'] = new StringReader();
macros[';'] = new CommentReader();
macros['^'] = new MetaReader();
macros['('] = new ListReader();
macros[')'] = new UnmatchedDelimiterReader();
macros['['] = new VectorReader();
macros[']'] = new UnmatchedDelimiterReader();
macros['{'] = new MapReader();
macros['}'] = new UnmatchedDelimiterReader();
macros['\\'] = new CharacterReader();
macros['#'] = new DispatchReader();
dispatchMacros['^'] = new MetaReader();
//dispatchMacros['"'] = new RegexReader();
dispatchMacros['{'] = new SetReader();
dispatchMacros['<'] = new UnreadableReader();
dispatchMacros['_'] = new DiscardReader();
}
static boolean nonConstituent(int ch){
return ch == '@' || ch == '`' || ch == '~';
}
static public Object readString(String s, IPersistentMap opts){
PushbackReader r = new PushbackReader(new java.io.StringReader(s));
return read(r, opts);
}
static boolean isWhitespace(int ch){
return Character.isWhitespace((char)ch) || ch == ',';
}
static void unread(PushbackReader r, int ch) {
if(ch != -1)
try
{
r.unread(ch);
}
catch(IOException e)
{
throw Util.sneakyThrow(e);
}
}
public static class ReaderException extends RuntimeException{
final int line;
final int column;
public ReaderException(int line, int column, Throwable cause){
super(cause);
this.line = line;
this.column = column;
}
}
static public int read1(Reader r){
try
{
return r.read();
}
catch(IOException e)
{
throw Util.sneakyThrow(e);
}
}
static final Keyword EOF = Keyword.intern(null,"eof");
static public Object read(PushbackReader r, IPersistentMap opts){
return read(r,!opts.containsKey(EOF),opts.valAt(EOF),false,opts);
}
static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive,
Object opts)
{
try
{
for(; ;)
{
int ch = read1(r);
while(isWhitespace(ch))
ch = read1(r);
if(ch == -1)
{
if(eofIsError)
throw Util.runtimeException("EOF while reading");
return eofValue;
}
if(Character.isDigit((char)ch))
{
Object n = readNumber(r, (char) ch);
if(RT.suppressRead())
return null;
return n;
}
IFn macroFn = getMacro(ch);
if(macroFn != null)
{
Object ret = macroFn.invoke(r, (char) ch, opts);
if(RT.suppressRead())
return null;
//no op macros return the reader
if(ret == r)
continue;
return ret;
}
if(ch == '+' || ch == '-')
{
int ch2 = read1(r);
if(Character.isDigit((char)ch2))
{
unread(r, ch2);
Object n = readNumber(r, (char) ch);
if(RT.suppressRead())
return null;
return n;
}
unread(r, ch2);
}
String token = readToken(r, (char) ch, true);
if(RT.suppressRead())
return null;
return interpretToken(token);
}
}
catch(Exception e)
{
if(isRecursive || !(r instanceof LineNumberingPushbackReader))
throw Util.sneakyThrow(e);
LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
//throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
}
}
static private String readToken(PushbackReader r, char initch, boolean leadConstituent) {
StringBuilder sb = new StringBuilder();
if(leadConstituent && nonConstituent(initch))
throw Util.runtimeException("Invalid leading character: " + (char)initch);
sb.append(initch);
for(; ;)
{
int ch = read1(r);
if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
{
unread(r, ch);
return sb.toString();
}
else if(nonConstituent(ch))
throw Util.runtimeException("Invalid constituent character: " + (char)ch);
sb.append((char) ch);
}
}
static private Object readNumber(PushbackReader r, char initch) {
StringBuilder sb = new StringBuilder();
sb.append(initch);
for(; ;)
{
int ch = read1(r);
if(ch == -1 || isWhitespace(ch) || isMacro(ch))
{
unread(r, ch);
break;
}
sb.append((char) ch);
}
String s = sb.toString();
Object n = matchNumber(s);
if(n == null)
throw new NumberFormatException("Invalid number: " + s);
return n;
}
static private int readUnicodeChar(String token, int offset, int length, int base) {
if(token.length() != offset + length)
throw new IllegalArgumentException("Invalid unicode character: \\" + token);
int uc = 0;
for(int i = offset; i < offset + length; ++i)
{
int d = Character.digit(token.charAt(i), base);
if(d == -1)
throw new IllegalArgumentException("Invalid digit: " + token.charAt(i));
uc = uc * base + d;
}
return (char) uc;
}
static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) {
int uc = Character.digit((char)initch, base);
if(uc == -1)
throw new IllegalArgumentException("Invalid digit: " + (char) initch);
int i = 1;
for(; i < length; ++i)
{
int ch = read1(r);
if(ch == -1 || isWhitespace(ch) || isMacro(ch))
{
unread(r, ch);
break;
}
int d = Character.digit((char)ch, base);
if(d == -1)
throw new IllegalArgumentException("Invalid digit: " + (char) ch);
uc = uc * base + d;
}
if(i != length && exact)
throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length);
return uc;
}
static private Object interpretToken(String s) {
if(s.equals("nil"))
{
return null;
}
else if(s.equals("true"))
{
return RT.T;
}
else if(s.equals("false"))
{
return RT.F;
}
Object ret = null;
ret = matchSymbol(s);
if(ret != null)
return ret;
throw Util.runtimeException("Invalid token: " + s);
}
private static Object matchSymbol(String s){
Matcher m = symbolPat.matcher(s);
if(m.matches())
{
int gc = m.groupCount();
String ns = m.group(1);
String name = m.group(2);
if(ns != null && ns.endsWith(":/")
|| name.endsWith(":")
|| s.indexOf("::", 1) != -1)
return null;
if(s.startsWith("::"))
{
return null;
}
boolean isKeyword = s.charAt(0) == ':';
Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
if(isKeyword)
return Keyword.intern(sym);
return sym;
}
return null;
}
private static Object matchNumber(String s){
Matcher m = intPat.matcher(s);
if(m.matches())
{
if(m.group(2) != null)
{
if(m.group(8) != null)
return BigInt.ZERO;
return Numbers.num(0);
}
boolean negate = (m.group(1).equals("-"));
String n;
int radix = 10;
if((n = m.group(3)) != null)
radix = 10;
else if((n = m.group(4)) != null)
radix = 16;
else if((n = m.group(5)) != null)
radix = 8;
else if((n = m.group(7)) != null)
radix = Integer.parseInt(m.group(6));
if(n == null)
return null;
BigInteger bn = new BigInteger(n, radix);
if(negate)
bn = bn.negate();
if(m.group(8) != null)
return BigInt.fromBigInteger(bn);
return bn.bitLength() < 64 ?
Numbers.num(bn.longValue())
: BigInt.fromBigInteger(bn);
}
m = floatPat.matcher(s);
if(m.matches())
{
if(m.group(4) != null)
return new BigDecimal(m.group(1));
return Double.parseDouble(s);
}
m = ratioPat.matcher(s);
if(m.matches())
{
String numerator = m.group(1);
if (numerator.startsWith("+")) numerator = numerator.substring(1);
return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(numerator))),
Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(2)))));
}
return null;
}
static private IFn getMacro(int ch){
if(ch < macros.length)
return macros[ch];
return null;
}
static private boolean isMacro(int ch){
return (ch < macros.length && macros[ch] != null);
}
static private boolean isTerminatingMacro(int ch){
return (ch != '#' && ch != '\'' && isMacro(ch));
}
/*
public static class RegexReader extends AFn{
static StringReader stringrdr = new StringReader();
public Object invoke(Object reader, Object doublequote) {
StringBuilder sb = new StringBuilder();
Reader r = (Reader) reader;
for(int ch = read1(r); ch != '"'; ch = read1(r))
{
if(ch == -1)
throw Util.runtimeException("EOF while reading regex");
sb.append( (char) ch );
if(ch == '\\') //escape
{
ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading regex");
sb.append( (char) ch ) ;
}
}
return Pattern.compile(sb.toString());
}
}
*/
public static class StringReader extends AFn{
public Object invoke(Object reader, Object doublequote, Object opts) {
StringBuilder sb = new StringBuilder();
Reader r = (Reader) reader;
for(int ch = read1(r); ch != '"'; ch = read1(r))
{
if(ch == -1)
throw Util.runtimeException("EOF while reading string");
if(ch == '\\') //escape
{
ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading string");
switch(ch)
{
case 't':
ch = '\t';
break;
case 'r':
ch = '\r';
break;
case 'n':
ch = '\n';
break;
case '\\':
break;
case '"':
break;
case 'b':
ch = '\b';
break;
case 'f':
ch = '\f';
break;
case 'u':
{
ch = read1(r);
if (Character.digit((char)ch, 16) == -1)
throw Util.runtimeException("Invalid unicode escape: \\u" + (char) ch);
ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
break;
}
default:
{
if(Character.isDigit((char)ch))
{
ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false);
if(ch > 0377)
throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
}
else
throw Util.runtimeException("Unsupported escape character: \\" + (char) ch);
}
}
}
sb.append((char) ch);
}
return sb.toString();
}
}
public static class CommentReader extends AFn{
public Object invoke(Object reader, Object semicolon, Object opts) {
Reader r = (Reader) reader;
int ch;
do
{
ch = read1(r);
} while(ch != -1 && ch != '\n' && ch != '\r');
return r;
}
}
public static class DiscardReader extends AFn{
public Object invoke(Object reader, Object underscore, Object opts) {
PushbackReader r = (PushbackReader) reader;
read(r, true, null, true, opts);
return r;
}
}
public static class DispatchReader extends AFn{
public Object invoke(Object reader, Object hash, Object opts) {
int ch = read1((Reader) reader);
if(ch == -1)
throw Util.runtimeException("EOF while reading character");
IFn fn = dispatchMacros[ch];
if(fn == null) {
//try tagged reader
if(Character.isLetter((char)ch))
{
unread((PushbackReader) reader, ch);
return taggedReader.invoke(reader, ch, opts);
}
throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
}
return fn.invoke(reader, ch, opts);
}
}
public static class MetaReader extends AFn{
public Object invoke(Object reader, Object caret, Object opts) {
PushbackReader r = (PushbackReader) reader;
int line = -1;
int column = -1;
if(r instanceof LineNumberingPushbackReader)
{
line = ((LineNumberingPushbackReader) r).getLineNumber();
column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
}
Object meta = read(r, true, null, true, opts);
if(meta instanceof Symbol || meta instanceof String)
meta = RT.map(RT.TAG_KEY, meta);
else if (meta instanceof Keyword)
meta = RT.map(meta, RT.T);
else if(!(meta instanceof IPersistentMap))
throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
Object o = read(r, true, null, true, opts);
if(o instanceof IMeta)
{
if(line != -1 && o instanceof ISeq)
{
meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line).assoc(RT.COLUMN_KEY, column);
}
if(o instanceof IReference)
{
((IReference)o).resetMeta((IPersistentMap) meta);
return o;
}
Object ometa = RT.meta(o);
for(ISeq s = RT.seq(meta); s != null; s = s.next()) {
IMapEntry kv = (IMapEntry) s.first();
ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
}
return ((IObj) o).withMeta((IPersistentMap) ometa);
}
else
throw new IllegalArgumentException("Metadata can only be applied to IMetas");
}
}
public static class CharacterReader extends AFn{
public Object invoke(Object reader, Object backslash, Object opts) {
PushbackReader r = (PushbackReader) reader;
int ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading character");
String token = readToken(r, (char) ch, false);
if(token.length() == 1)
return Character.valueOf(token.charAt(0));
else if(token.equals("newline"))
return '\n';
else if(token.equals("space"))
return ' ';
else if(token.equals("tab"))
return '\t';
else if(token.equals("backspace"))
return '\b';
else if(token.equals("formfeed"))
return '\f';
else if(token.equals("return"))
return '\r';
else if(token.startsWith("u"))
{
char c = (char) readUnicodeChar(token, 1, 4, 16);
if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
return c;
}
else if(token.startsWith("o"))
{
int len = token.length() - 1;
if(len > 3)
throw Util.runtimeException("Invalid octal escape sequence length: " + len);
int uc = readUnicodeChar(token, 1, len, 8);
if(uc > 0377)
throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
return (char) uc;
}
throw Util.runtimeException("Unsupported character: \\" + token);
}
}
public static class ListReader extends AFn{
public Object invoke(Object reader, Object leftparen, Object opts) {
PushbackReader r = (PushbackReader) reader;
int line = -1;
int column = -1;
if(r instanceof LineNumberingPushbackReader)
{
line = ((LineNumberingPushbackReader) r).getLineNumber();
column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
}
List list = readDelimitedList(')', r, true, opts);
if(list.isEmpty())
return PersistentList.EMPTY;
IObj s = (IObj) PersistentList.create(list);
// IObj s = (IObj) RT.seq(list);
// if(line != -1)
// {
// return s.withMeta(RT.map(RT.LINE_KEY, line, RT.COLUMN_KEY, column));
// }
// else
return s;
}
}
public static class VectorReader extends AFn{
public Object invoke(Object reader, Object leftparen, Object opts) {
PushbackReader r = (PushbackReader) reader;
return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts));
}
}
public static class MapReader extends AFn{
public Object invoke(Object reader, Object leftparen, Object opts) {
PushbackReader r = (PushbackReader) reader;
Object[] a = readDelimitedList('}', r, true, opts).toArray();
if((a.length & 1) == 1)
throw Util.runtimeException("Map literal must contain an even number of forms");
return RT.map(a);
}
}
public static class SetReader extends AFn{
public Object invoke(Object reader, Object leftbracket, Object opts) {
PushbackReader r = (PushbackReader) reader;
return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true, opts));
}
}
public static class UnmatchedDelimiterReader extends AFn{
public Object invoke(Object reader, Object rightdelim, Object opts) {
throw Util.runtimeException("Unmatched delimiter: " + rightdelim);
}
}
public static class UnreadableReader extends AFn{
public Object invoke(Object reader, Object leftangle, Object opts) {
throw Util.runtimeException("Unreadable form");
}
}
public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive, Object opts) {
final int firstline =
(r instanceof LineNumberingPushbackReader) ?
((LineNumberingPushbackReader) r).getLineNumber() : -1;
ArrayList a = new ArrayList();
for(; ;)
{
int ch = read1(r);
while(isWhitespace(ch))
ch = read1(r);
if(ch == -1)
{
if(firstline < 0)
throw Util.runtimeException("EOF while reading");
else
throw Util.runtimeException("EOF while reading, starting at line " + firstline);
}
if(ch == delim)
break;
IFn macroFn = getMacro(ch);
if(macroFn != null)
{
Object mret = macroFn.invoke(r, (char) ch, opts);
//no op macros return the reader
if(mret != r)
a.add(mret);
}
else
{
unread(r, ch);
Object o = read(r, true, null, isRecursive, opts);
if(o != r)
a.add(o);
}
}
return a;
}
public static class TaggedReader extends AFn{
public Object invoke(Object reader, Object firstChar, Object opts){
PushbackReader r = (PushbackReader) reader;
Object name = read(r, true, null, false, opts);
if (!(name instanceof Symbol))
throw new RuntimeException("Reader tag must be a symbol");
Symbol sym = (Symbol)name;
return readTagged(r, sym, (IPersistentMap) opts);
}
static Keyword READERS = Keyword.intern(null,"readers");
static Keyword DEFAULT = Keyword.intern(null,"default");
private Object readTagged(PushbackReader reader, Symbol tag, IPersistentMap opts){
Object o = read(reader, true, null, true, opts);
ILookup readers = (ILookup)RT.get(opts, READERS);
IFn dataReader = (IFn)RT.get(readers, tag);
if(dataReader == null)
dataReader = (IFn)RT.get(RT.DEFAULT_DATA_READERS.deref(),tag);
if(dataReader == null){
IFn defaultReader = (IFn)RT.get(opts, DEFAULT);
if(defaultReader != null)
return defaultReader.invoke(tag, o);
else
throw new RuntimeException("No reader function for tag " + tag.toString());
}
else
return dataReader.invoke(o);
}
}
}
================================================
FILE: src/jvm/clojure/lang/EnumerationSeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.Enumeration;
public class EnumerationSeq extends ASeq{
final Enumeration iter;
final State state;
static class State{
volatile Object val;
volatile Object _rest;
}
public static EnumerationSeq create(Enumeration iter){
if(iter.hasMoreElements())
return new EnumerationSeq(iter);
return null;
}
EnumerationSeq(Enumeration iter){
this.iter = iter;
state = new State();
this.state.val = state;
this.state._rest = state;
}
EnumerationSeq(IPersistentMap meta, Enumeration iter, State state){
super(meta);
this.iter = iter;
this.state = state;
}
public Object first(){
if(state.val == state)
synchronized(state)
{
if(state.val == state)
state.val = iter.nextElement();
}
return state.val;
}
public ISeq next(){
if(state._rest == state)
synchronized(state)
{
if(state._rest == state)
{
first();
state._rest = create(iter);
}
}
return (ISeq) state._rest;
}
public EnumerationSeq withMeta(IPersistentMap meta){
return new EnumerationSeq(meta, iter, state);
}
private void writeObject (java.io.ObjectOutputStream out) throws IOException {
throw new NotSerializableException(getClass().getName());
}
}
================================================
FILE: src/jvm/clojure/lang/ExceptionInfo.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
/**
* Exception that carries data (a map) as additional payload. Clojure programs that need
* richer semantics for exceptions should use this in lieu of defining project-specific
* exception classes.
*/
public class ExceptionInfo extends RuntimeException implements IExceptionInfo {
public final IPersistentMap data;
public ExceptionInfo(String s, IPersistentMap data) {
super(s);
if (data instanceof IPersistentMap) {
this.data = data;
} else {
throw new IllegalArgumentException("Additional data must be a persistent map: " + data);
}
}
public ExceptionInfo(String s, IPersistentMap data, Throwable throwable) {
super(s, throwable);
this.data = data;
}
public IPersistentMap getData() {
return data;
}
public String toString() {
return "clojure.lang.ExceptionInfo: " + getMessage() + " " + data.toString();
}
}
================================================
FILE: src/jvm/clojure/lang/Fn.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 25, 2008 */
package clojure.lang;
public interface Fn{
}
================================================
FILE: src/jvm/clojure/lang/FnLoaderThunk.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich 2/28/11 */
package clojure.lang;
public class FnLoaderThunk extends RestFn{
final Var v;
final ClassLoader loader;
final String fnClassName;
IFn fn;
public FnLoaderThunk(Var v, String fnClassName){
this.v = v;
this.loader = (ClassLoader) RT.FN_LOADER_VAR.get();
this.fnClassName = fnClassName;
fn = null;
}
public Object invoke(Object arg1) {
load();
return fn.invoke(arg1);
}
public Object invoke(Object arg1, Object arg2) {
load();
return fn.invoke(arg1,arg2);
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
load();
return fn.invoke(arg1,arg2,arg3);
}
protected Object doInvoke(Object args) {
load();
return fn.applyTo((ISeq) args);
}
private void load() {
if(fn == null)
{
try
{
fn = (IFn) Class.forName(fnClassName,true,loader).newInstance();
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
v.root = fn;
}
}
public int getRequiredArity(){
return 0;
}
public IObj withMeta(IPersistentMap meta){
return this;
}
public IPersistentMap meta(){
return null;
}
}
================================================
FILE: src/jvm/clojure/lang/IAtom.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Aug 2, 2009 */
package clojure.lang;
public interface IAtom{
Object swap(IFn f);
Object swap(IFn f, Object arg);
Object swap(IFn f, Object arg1, Object arg2);
Object swap(IFn f, Object x, Object y, ISeq args);
boolean compareAndSet(Object oldv, Object newv);
Object reset(Object newval);
}
================================================
FILE: src/jvm/clojure/lang/IBlockingDeref.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich 3/18/11 */
package clojure.lang;
public interface IBlockingDeref{
Object deref(long ms, Object timeoutValue) ;
}
================================================
FILE: src/jvm/clojure/lang/IChunk.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jun 18, 2009 */
package clojure.lang;
public interface IChunk extends Indexed{
IChunk dropFirst();
Object reduce(IFn f, Object start) ;
}
================================================
FILE: src/jvm/clojure/lang/IChunkedSeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich May 24, 2009 */
package clojure.lang;
public interface IChunkedSeq extends ISeq, Sequential {
IChunk chunkedFirst() ;
ISeq chunkedNext() ;
ISeq chunkedMore() ;
}
================================================
FILE: src/jvm/clojure/lang/IDeref.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Feb 9, 2009 */
package clojure.lang;
public interface IDeref{
Object deref() ;
}
================================================
FILE: src/jvm/clojure/lang/IEditableCollection.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 17, 2009 */
package clojure.lang;
public interface IEditableCollection{
ITransientCollection asTransient();
}
================================================
FILE: src/jvm/clojure/lang/IExceptionInfo.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
/**
* Interface for exceptions that carry data (a map) as additional payload. Clojure
* programs that need richer semantics for exceptions should use this in lieu of
* defining project-specific exception classes.
*/
public interface IExceptionInfo {
public IPersistentMap getData();
}
================================================
FILE: src/jvm/clojure/lang/IFn.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 3:54:03 PM */
package clojure.lang;
import java.util.concurrent.Callable;
/**
*
IFn provides complete access to invoking
* any of Clojure's APIs.
* You can also access any other library written in Clojure, after adding
* either its source or compiled form to the classpath.
*/
public interface IFn extends Callable, Runnable{
public Object invoke() ;
public Object invoke(Object arg1) ;
public Object invoke(Object arg1, Object arg2) ;
public Object invoke(Object arg1, Object arg2, Object arg3) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) ;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
;
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
Object... args)
;
public Object applyTo(ISeq arglist) ;
static public interface L{long invokePrim();}
static public interface D{double invokePrim();}
static public interface OL{long invokePrim(Object arg0);}
static public interface OD{double invokePrim(Object arg0);}
static public interface LO{Object invokePrim(long arg0);}
static public interface LL{long invokePrim(long arg0);}
static public interface LD{double invokePrim(long arg0);}
static public interface DO{Object invokePrim(double arg0);}
static public interface DL{long invokePrim(double arg0);}
static public interface DD{double invokePrim(double arg0);}
static public interface OOL{long invokePrim(Object arg0, Object arg1);}
static public interface OOD{double invokePrim(Object arg0, Object arg1);}
static public interface OLO{Object invokePrim(Object arg0, long arg1);}
static public interface OLL{long invokePrim(Object arg0, long arg1);}
static public interface OLD{double invokePrim(Object arg0, long arg1);}
static public interface ODO{Object invokePrim(Object arg0, double arg1);}
static public interface ODL{long invokePrim(Object arg0, double arg1);}
static public interface ODD{double invokePrim(Object arg0, double arg1);}
static public interface LOO{Object invokePrim(long arg0, Object arg1);}
static public interface LOL{long invokePrim(long arg0, Object arg1);}
static public interface LOD{double invokePrim(long arg0, Object arg1);}
static public interface LLO{Object invokePrim(long arg0, long arg1);}
static public interface LLL{long invokePrim(long arg0, long arg1);}
static public interface LLD{double invokePrim(long arg0, long arg1);}
static public interface LDO{Object invokePrim(long arg0, double arg1);}
static public interface LDL{long invokePrim(long arg0, double arg1);}
static public interface LDD{double invokePrim(long arg0, double arg1);}
static public interface DOO{Object invokePrim(double arg0, Object arg1);}
static public interface DOL{long invokePrim(double arg0, Object arg1);}
static public interface DOD{double invokePrim(double arg0, Object arg1);}
static public interface DLO{Object invokePrim(double arg0, long arg1);}
static public interface DLL{long invokePrim(double arg0, long arg1);}
static public interface DLD{double invokePrim(double arg0, long arg1);}
static public interface DDO{Object invokePrim(double arg0, double arg1);}
static public interface DDL{long invokePrim(double arg0, double arg1);}
static public interface DDD{double invokePrim(double arg0, double arg1);}
static public interface OOOL{long invokePrim(Object arg0, Object arg1, Object arg2);}
static public interface OOOD{double invokePrim(Object arg0, Object arg1, Object arg2);}
static public interface OOLO{Object invokePrim(Object arg0, Object arg1, long arg2);}
static public interface OOLL{long invokePrim(Object arg0, Object arg1, long arg2);}
static public interface OOLD{double invokePrim(Object arg0, Object arg1, long arg2);}
static public interface OODO{Object invokePrim(Object arg0, Object arg1, double arg2);}
static public interface OODL{long invokePrim(Object arg0, Object arg1, double arg2);}
static public interface OODD{double invokePrim(Object arg0, Object arg1, double arg2);}
static public interface OLOO{Object invokePrim(Object arg0, long arg1, Object arg2);}
static public interface OLOL{long invokePrim(Object arg0, long arg1, Object arg2);}
static public interface OLOD{double invokePrim(Object arg0, long arg1, Object arg2);}
static public interface OLLO{Object invokePrim(Object arg0, long arg1, long arg2);}
static public interface OLLL{long invokePrim(Object arg0, long arg1, long arg2);}
static public interface OLLD{double invokePrim(Object arg0, long arg1, long arg2);}
static public interface OLDO{Object invokePrim(Object arg0, long arg1, double arg2);}
static public interface OLDL{long invokePrim(Object arg0, long arg1, double arg2);}
static public interface OLDD{double invokePrim(Object arg0, long arg1, double arg2);}
static public interface ODOO{Object invokePrim(Object arg0, double arg1, Object arg2);}
static public interface ODOL{long invokePrim(Object arg0, double arg1, Object arg2);}
static public interface ODOD{double invokePrim(Object arg0, double arg1, Object arg2);}
static public interface ODLO{Object invokePrim(Object arg0, double arg1, long arg2);}
static public interface ODLL{long invokePrim(Object arg0, double arg1, long arg2);}
static public interface ODLD{double invokePrim(Object arg0, double arg1, long arg2);}
static public interface ODDO{Object invokePrim(Object arg0, double arg1, double arg2);}
static public interface ODDL{long invokePrim(Object arg0, double arg1, double arg2);}
static public interface ODDD{double invokePrim(Object arg0, double arg1, double arg2);}
static public interface LOOO{Object invokePrim(long arg0, Object arg1, Object arg2);}
static public interface LOOL{long invokePrim(long arg0, Object arg1, Object arg2);}
static public interface LOOD{double invokePrim(long arg0, Object arg1, Object arg2);}
static public interface LOLO{Object invokePrim(long arg0, Object arg1, long arg2);}
static public interface LOLL{long invokePrim(long arg0, Object arg1, long arg2);}
static public interface LOLD{double invokePrim(long arg0, Object arg1, long arg2);}
static public interface LODO{Object invokePrim(long arg0, Object arg1, double arg2);}
static public interface LODL{long invokePrim(long arg0, Object arg1, double arg2);}
static public interface LODD{double invokePrim(long arg0, Object arg1, double arg2);}
static public interface LLOO{Object invokePrim(long arg0, long arg1, Object arg2);}
static public interface LLOL{long invokePrim(long arg0, long arg1, Object arg2);}
static public interface LLOD{double invokePrim(long arg0, long arg1, Object arg2);}
static public interface LLLO{Object invokePrim(long arg0, long arg1, long arg2);}
static public interface LLLL{long invokePrim(long arg0, long arg1, long arg2);}
static public interface LLLD{double invokePrim(long arg0, long arg1, long arg2);}
static public interface LLDO{Object invokePrim(long arg0, long arg1, double arg2);}
static public interface LLDL{long invokePrim(long arg0, long arg1, double arg2);}
static public interface LLDD{double invokePrim(long arg0, long arg1, double arg2);}
static public interface LDOO{Object invokePrim(long arg0, double arg1, Object arg2);}
static public interface LDOL{long invokePrim(long arg0, double arg1, Object arg2);}
static public interface LDOD{double invokePrim(long arg0, double arg1, Object arg2);}
static public interface LDLO{Object invokePrim(long arg0, double arg1, long arg2);}
static public interface LDLL{long invokePrim(long arg0, double arg1, long arg2);}
static public interface LDLD{double invokePrim(long arg0, double arg1, long arg2);}
static public interface LDDO{Object invokePrim(long arg0, double arg1, double arg2);}
static public interface LDDL{long invokePrim(long arg0, double arg1, double arg2);}
static public interface LDDD{double invokePrim(long arg0, double arg1, double arg2);}
static public interface DOOO{Object invokePrim(double arg0, Object arg1, Object arg2);}
static public interface DOOL{long invokePrim(double arg0, Object arg1, Object arg2);}
static public interface DOOD{double invokePrim(double arg0, Object arg1, Object arg2);}
static public interface DOLO{Object invokePrim(double arg0, Object arg1, long arg2);}
static public interface DOLL{long invokePrim(double arg0, Object arg1, long arg2);}
static public interface DOLD{double invokePrim(double arg0, Object arg1, long arg2);}
static public interface DODO{Object invokePrim(double arg0, Object arg1, double arg2);}
static public interface DODL{long invokePrim(double arg0, Object arg1, double arg2);}
static public interface DODD{double invokePrim(double arg0, Object arg1, double arg2);}
static public interface DLOO{Object invokePrim(double arg0, long arg1, Object arg2);}
static public interface DLOL{long invokePrim(double arg0, long arg1, Object arg2);}
static public interface DLOD{double invokePrim(double arg0, long arg1, Object arg2);}
static public interface DLLO{Object invokePrim(double arg0, long arg1, long arg2);}
static public interface DLLL{long invokePrim(double arg0, long arg1, long arg2);}
static public interface DLLD{double invokePrim(double arg0, long arg1, long arg2);}
static public interface DLDO{Object invokePrim(double arg0, long arg1, double arg2);}
static public interface DLDL{long invokePrim(double arg0, long arg1, double arg2);}
static public interface DLDD{double invokePrim(double arg0, long arg1, double arg2);}
static public interface DDOO{Object invokePrim(double arg0, double arg1, Object arg2);}
static public interface DDOL{long invokePrim(double arg0, double arg1, Object arg2);}
static public interface DDOD{double invokePrim(double arg0, double arg1, Object arg2);}
static public interface DDLO{Object invokePrim(double arg0, double arg1, long arg2);}
static public interface DDLL{long invokePrim(double arg0, double arg1, long arg2);}
static public interface DDLD{double invokePrim(double arg0, double arg1, long arg2);}
static public interface DDDO{Object invokePrim(double arg0, double arg1, double arg2);}
static public interface DDDL{long invokePrim(double arg0, double arg1, double arg2);}
static public interface DDDD{double invokePrim(double arg0, double arg1, double arg2);}
static public interface OOOOL{long invokePrim(Object arg0, Object arg1, Object arg2, Object arg3);}
static public interface OOOOD{double invokePrim(Object arg0, Object arg1, Object arg2, Object arg3);}
static public interface OOOLO{Object invokePrim(Object arg0, Object arg1, Object arg2, long arg3);}
static public interface OOOLL{long invokePrim(Object arg0, Object arg1, Object arg2, long arg3);}
static public interface OOOLD{double invokePrim(Object arg0, Object arg1, Object arg2, long arg3);}
static public interface OOODO{Object invokePrim(Object arg0, Object arg1, Object arg2, double arg3);}
static public interface OOODL{long invokePrim(Object arg0, Object arg1, Object arg2, double arg3);}
static public interface OOODD{double invokePrim(Object arg0, Object arg1, Object arg2, double arg3);}
static public interface OOLOO{Object invokePrim(Object arg0, Object arg1, long arg2, Object arg3);}
static public interface OOLOL{long invokePrim(Object arg0, Object arg1, long arg2, Object arg3);}
static public interface OOLOD{double invokePrim(Object arg0, Object arg1, long arg2, Object arg3);}
static public interface OOLLO{Object invokePrim(Object arg0, Object arg1, long arg2, long arg3);}
static public interface OOLLL{long invokePrim(Object arg0, Object arg1, long arg2, long arg3);}
static public interface OOLLD{double invokePrim(Object arg0, Object arg1, long arg2, long arg3);}
static public interface OOLDO{Object invokePrim(Object arg0, Object arg1, long arg2, double arg3);}
static public interface OOLDL{long invokePrim(Object arg0, Object arg1, long arg2, double arg3);}
static public interface OOLDD{double invokePrim(Object arg0, Object arg1, long arg2, double arg3);}
static public interface OODOO{Object invokePrim(Object arg0, Object arg1, double arg2, Object arg3);}
static public interface OODOL{long invokePrim(Object arg0, Object arg1, double arg2, Object arg3);}
static public interface OODOD{double invokePrim(Object arg0, Object arg1, double arg2, Object arg3);}
static public interface OODLO{Object invokePrim(Object arg0, Object arg1, double arg2, long arg3);}
static public interface OODLL{long invokePrim(Object arg0, Object arg1, double arg2, long arg3);}
static public interface OODLD{double invokePrim(Object arg0, Object arg1, double arg2, long arg3);}
static public interface OODDO{Object invokePrim(Object arg0, Object arg1, double arg2, double arg3);}
static public interface OODDL{long invokePrim(Object arg0, Object arg1, double arg2, double arg3);}
static public interface OODDD{double invokePrim(Object arg0, Object arg1, double arg2, double arg3);}
static public interface OLOOO{Object invokePrim(Object arg0, long arg1, Object arg2, Object arg3);}
static public interface OLOOL{long invokePrim(Object arg0, long arg1, Object arg2, Object arg3);}
static public interface OLOOD{double invokePrim(Object arg0, long arg1, Object arg2, Object arg3);}
static public interface OLOLO{Object invokePrim(Object arg0, long arg1, Object arg2, long arg3);}
static public interface OLOLL{long invokePrim(Object arg0, long arg1, Object arg2, long arg3);}
static public interface OLOLD{double invokePrim(Object arg0, long arg1, Object arg2, long arg3);}
static public interface OLODO{Object invokePrim(Object arg0, long arg1, Object arg2, double arg3);}
static public interface OLODL{long invokePrim(Object arg0, long arg1, Object arg2, double arg3);}
static public interface OLODD{double invokePrim(Object arg0, long arg1, Object arg2, double arg3);}
static public interface OLLOO{Object invokePrim(Object arg0, long arg1, long arg2, Object arg3);}
static public interface OLLOL{long invokePrim(Object arg0, long arg1, long arg2, Object arg3);}
static public interface OLLOD{double invokePrim(Object arg0, long arg1, long arg2, Object arg3);}
static public interface OLLLO{Object invokePrim(Object arg0, long arg1, long arg2, long arg3);}
static public interface OLLLL{long invokePrim(Object arg0, long arg1, long arg2, long arg3);}
static public interface OLLLD{double invokePrim(Object arg0, long arg1, long arg2, long arg3);}
static public interface OLLDO{Object invokePrim(Object arg0, long arg1, long arg2, double arg3);}
static public interface OLLDL{long invokePrim(Object arg0, long arg1, long arg2, double arg3);}
static public interface OLLDD{double invokePrim(Object arg0, long arg1, long arg2, double arg3);}
static public interface OLDOO{Object invokePrim(Object arg0, long arg1, double arg2, Object arg3);}
static public interface OLDOL{long invokePrim(Object arg0, long arg1, double arg2, Object arg3);}
static public interface OLDOD{double invokePrim(Object arg0, long arg1, double arg2, Object arg3);}
static public interface OLDLO{Object invokePrim(Object arg0, long arg1, double arg2, long arg3);}
static public interface OLDLL{long invokePrim(Object arg0, long arg1, double arg2, long arg3);}
static public interface OLDLD{double invokePrim(Object arg0, long arg1, double arg2, long arg3);}
static public interface OLDDO{Object invokePrim(Object arg0, long arg1, double arg2, double arg3);}
static public interface OLDDL{long invokePrim(Object arg0, long arg1, double arg2, double arg3);}
static public interface OLDDD{double invokePrim(Object arg0, long arg1, double arg2, double arg3);}
static public interface ODOOO{Object invokePrim(Object arg0, double arg1, Object arg2, Object arg3);}
static public interface ODOOL{long invokePrim(Object arg0, double arg1, Object arg2, Object arg3);}
static public interface ODOOD{double invokePrim(Object arg0, double arg1, Object arg2, Object arg3);}
static public interface ODOLO{Object invokePrim(Object arg0, double arg1, Object arg2, long arg3);}
static public interface ODOLL{long invokePrim(Object arg0, double arg1, Object arg2, long arg3);}
static public interface ODOLD{double invokePrim(Object arg0, double arg1, Object arg2, long arg3);}
static public interface ODODO{Object invokePrim(Object arg0, double arg1, Object arg2, double arg3);}
static public interface ODODL{long invokePrim(Object arg0, double arg1, Object arg2, double arg3);}
static public interface ODODD{double invokePrim(Object arg0, double arg1, Object arg2, double arg3);}
static public interface ODLOO{Object invokePrim(Object arg0, double arg1, long arg2, Object arg3);}
static public interface ODLOL{long invokePrim(Object arg0, double arg1, long arg2, Object arg3);}
static public interface ODLOD{double invokePrim(Object arg0, double arg1, long arg2, Object arg3);}
static public interface ODLLO{Object invokePrim(Object arg0, double arg1, long arg2, long arg3);}
static public interface ODLLL{long invokePrim(Object arg0, double arg1, long arg2, long arg3);}
static public interface ODLLD{double invokePrim(Object arg0, double arg1, long arg2, long arg3);}
static public interface ODLDO{Object invokePrim(Object arg0, double arg1, long arg2, double arg3);}
static public interface ODLDL{long invokePrim(Object arg0, double arg1, long arg2, double arg3);}
static public interface ODLDD{double invokePrim(Object arg0, double arg1, long arg2, double arg3);}
static public interface ODDOO{Object invokePrim(Object arg0, double arg1, double arg2, Object arg3);}
static public interface ODDOL{long invokePrim(Object arg0, double arg1, double arg2, Object arg3);}
static public interface ODDOD{double invokePrim(Object arg0, double arg1, double arg2, Object arg3);}
static public interface ODDLO{Object invokePrim(Object arg0, double arg1, double arg2, long arg3);}
static public interface ODDLL{long invokePrim(Object arg0, double arg1, double arg2, long arg3);}
static public interface ODDLD{double invokePrim(Object arg0, double arg1, double arg2, long arg3);}
static public interface ODDDO{Object invokePrim(Object arg0, double arg1, double arg2, double arg3);}
static public interface ODDDL{long invokePrim(Object arg0, double arg1, double arg2, double arg3);}
static public interface ODDDD{double invokePrim(Object arg0, double arg1, double arg2, double arg3);}
static public interface LOOOO{Object invokePrim(long arg0, Object arg1, Object arg2, Object arg3);}
static public interface LOOOL{long invokePrim(long arg0, Object arg1, Object arg2, Object arg3);}
static public interface LOOOD{double invokePrim(long arg0, Object arg1, Object arg2, Object arg3);}
static public interface LOOLO{Object invokePrim(long arg0, Object arg1, Object arg2, long arg3);}
static public interface LOOLL{long invokePrim(long arg0, Object arg1, Object arg2, long arg3);}
static public interface LOOLD{double invokePrim(long arg0, Object arg1, Object arg2, long arg3);}
static public interface LOODO{Object invokePrim(long arg0, Object arg1, Object arg2, double arg3);}
static public interface LOODL{long invokePrim(long arg0, Object arg1, Object arg2, double arg3);}
static public interface LOODD{double invokePrim(long arg0, Object arg1, Object arg2, double arg3);}
static public interface LOLOO{Object invokePrim(long arg0, Object arg1, long arg2, Object arg3);}
static public interface LOLOL{long invokePrim(long arg0, Object arg1, long arg2, Object arg3);}
static public interface LOLOD{double invokePrim(long arg0, Object arg1, long arg2, Object arg3);}
static public interface LOLLO{Object invokePrim(long arg0, Object arg1, long arg2, long arg3);}
static public interface LOLLL{long invokePrim(long arg0, Object arg1, long arg2, long arg3);}
static public interface LOLLD{double invokePrim(long arg0, Object arg1, long arg2, long arg3);}
static public interface LOLDO{Object invokePrim(long arg0, Object arg1, long arg2, double arg3);}
static public interface LOLDL{long invokePrim(long arg0, Object arg1, long arg2, double arg3);}
static public interface LOLDD{double invokePrim(long arg0, Object arg1, long arg2, double arg3);}
static public interface LODOO{Object invokePrim(long arg0, Object arg1, double arg2, Object arg3);}
static public interface LODOL{long invokePrim(long arg0, Object arg1, double arg2, Object arg3);}
static public interface LODOD{double invokePrim(long arg0, Object arg1, double arg2, Object arg3);}
static public interface LODLO{Object invokePrim(long arg0, Object arg1, double arg2, long arg3);}
static public interface LODLL{long invokePrim(long arg0, Object arg1, double arg2, long arg3);}
static public interface LODLD{double invokePrim(long arg0, Object arg1, double arg2, long arg3);}
static public interface LODDO{Object invokePrim(long arg0, Object arg1, double arg2, double arg3);}
static public interface LODDL{long invokePrim(long arg0, Object arg1, double arg2, double arg3);}
static public interface LODDD{double invokePrim(long arg0, Object arg1, double arg2, double arg3);}
static public interface LLOOO{Object invokePrim(long arg0, long arg1, Object arg2, Object arg3);}
static public interface LLOOL{long invokePrim(long arg0, long arg1, Object arg2, Object arg3);}
static public interface LLOOD{double invokePrim(long arg0, long arg1, Object arg2, Object arg3);}
static public interface LLOLO{Object invokePrim(long arg0, long arg1, Object arg2, long arg3);}
static public interface LLOLL{long invokePrim(long arg0, long arg1, Object arg2, long arg3);}
static public interface LLOLD{double invokePrim(long arg0, long arg1, Object arg2, long arg3);}
static public interface LLODO{Object invokePrim(long arg0, long arg1, Object arg2, double arg3);}
static public interface LLODL{long invokePrim(long arg0, long arg1, Object arg2, double arg3);}
static public interface LLODD{double invokePrim(long arg0, long arg1, Object arg2, double arg3);}
static public interface LLLOO{Object invokePrim(long arg0, long arg1, long arg2, Object arg3);}
static public interface LLLOL{long invokePrim(long arg0, long arg1, long arg2, Object arg3);}
static public interface LLLOD{double invokePrim(long arg0, long arg1, long arg2, Object arg3);}
static public interface LLLLO{Object invokePrim(long arg0, long arg1, long arg2, long arg3);}
static public interface LLLLL{long invokePrim(long arg0, long arg1, long arg2, long arg3);}
static public interface LLLLD{double invokePrim(long arg0, long arg1, long arg2, long arg3);}
static public interface LLLDO{Object invokePrim(long arg0, long arg1, long arg2, double arg3);}
static public interface LLLDL{long invokePrim(long arg0, long arg1, long arg2, double arg3);}
static public interface LLLDD{double invokePrim(long arg0, long arg1, long arg2, double arg3);}
static public interface LLDOO{Object invokePrim(long arg0, long arg1, double arg2, Object arg3);}
static public interface LLDOL{long invokePrim(long arg0, long arg1, double arg2, Object arg3);}
static public interface LLDOD{double invokePrim(long arg0, long arg1, double arg2, Object arg3);}
static public interface LLDLO{Object invokePrim(long arg0, long arg1, double arg2, long arg3);}
static public interface LLDLL{long invokePrim(long arg0, long arg1, double arg2, long arg3);}
static public interface LLDLD{double invokePrim(long arg0, long arg1, double arg2, long arg3);}
static public interface LLDDO{Object invokePrim(long arg0, long arg1, double arg2, double arg3);}
static public interface LLDDL{long invokePrim(long arg0, long arg1, double arg2, double arg3);}
static public interface LLDDD{double invokePrim(long arg0, long arg1, double arg2, double arg3);}
static public interface LDOOO{Object invokePrim(long arg0, double arg1, Object arg2, Object arg3);}
static public interface LDOOL{long invokePrim(long arg0, double arg1, Object arg2, Object arg3);}
static public interface LDOOD{double invokePrim(long arg0, double arg1, Object arg2, Object arg3);}
static public interface LDOLO{Object invokePrim(long arg0, double arg1, Object arg2, long arg3);}
static public interface LDOLL{long invokePrim(long arg0, double arg1, Object arg2, long arg3);}
static public interface LDOLD{double invokePrim(long arg0, double arg1, Object arg2, long arg3);}
static public interface LDODO{Object invokePrim(long arg0, double arg1, Object arg2, double arg3);}
static public interface LDODL{long invokePrim(long arg0, double arg1, Object arg2, double arg3);}
static public interface LDODD{double invokePrim(long arg0, double arg1, Object arg2, double arg3);}
static public interface LDLOO{Object invokePrim(long arg0, double arg1, long arg2, Object arg3);}
static public interface LDLOL{long invokePrim(long arg0, double arg1, long arg2, Object arg3);}
static public interface LDLOD{double invokePrim(long arg0, double arg1, long arg2, Object arg3);}
static public interface LDLLO{Object invokePrim(long arg0, double arg1, long arg2, long arg3);}
static public interface LDLLL{long invokePrim(long arg0, double arg1, long arg2, long arg3);}
static public interface LDLLD{double invokePrim(long arg0, double arg1, long arg2, long arg3);}
static public interface LDLDO{Object invokePrim(long arg0, double arg1, long arg2, double arg3);}
static public interface LDLDL{long invokePrim(long arg0, double arg1, long arg2, double arg3);}
static public interface LDLDD{double invokePrim(long arg0, double arg1, long arg2, double arg3);}
static public interface LDDOO{Object invokePrim(long arg0, double arg1, double arg2, Object arg3);}
static public interface LDDOL{long invokePrim(long arg0, double arg1, double arg2, Object arg3);}
static public interface LDDOD{double invokePrim(long arg0, double arg1, double arg2, Object arg3);}
static public interface LDDLO{Object invokePrim(long arg0, double arg1, double arg2, long arg3);}
static public interface LDDLL{long invokePrim(long arg0, double arg1, double arg2, long arg3);}
static public interface LDDLD{double invokePrim(long arg0, double arg1, double arg2, long arg3);}
static public interface LDDDO{Object invokePrim(long arg0, double arg1, double arg2, double arg3);}
static public interface LDDDL{long invokePrim(long arg0, double arg1, double arg2, double arg3);}
static public interface LDDDD{double invokePrim(long arg0, double arg1, double arg2, double arg3);}
static public interface DOOOO{Object invokePrim(double arg0, Object arg1, Object arg2, Object arg3);}
static public interface DOOOL{long invokePrim(double arg0, Object arg1, Object arg2, Object arg3);}
static public interface DOOOD{double invokePrim(double arg0, Object arg1, Object arg2, Object arg3);}
static public interface DOOLO{Object invokePrim(double arg0, Object arg1, Object arg2, long arg3);}
static public interface DOOLL{long invokePrim(double arg0, Object arg1, Object arg2, long arg3);}
static public interface DOOLD{double invokePrim(double arg0, Object arg1, Object arg2, long arg3);}
static public interface DOODO{Object invokePrim(double arg0, Object arg1, Object arg2, double arg3);}
static public interface DOODL{long invokePrim(double arg0, Object arg1, Object arg2, double arg3);}
static public interface DOODD{double invokePrim(double arg0, Object arg1, Object arg2, double arg3);}
static public interface DOLOO{Object invokePrim(double arg0, Object arg1, long arg2, Object arg3);}
static public interface DOLOL{long invokePrim(double arg0, Object arg1, long arg2, Object arg3);}
static public interface DOLOD{double invokePrim(double arg0, Object arg1, long arg2, Object arg3);}
static public interface DOLLO{Object invokePrim(double arg0, Object arg1, long arg2, long arg3);}
static public interface DOLLL{long invokePrim(double arg0, Object arg1, long arg2, long arg3);}
static public interface DOLLD{double invokePrim(double arg0, Object arg1, long arg2, long arg3);}
static public interface DOLDO{Object invokePrim(double arg0, Object arg1, long arg2, double arg3);}
static public interface DOLDL{long invokePrim(double arg0, Object arg1, long arg2, double arg3);}
static public interface DOLDD{double invokePrim(double arg0, Object arg1, long arg2, double arg3);}
static public interface DODOO{Object invokePrim(double arg0, Object arg1, double arg2, Object arg3);}
static public interface DODOL{long invokePrim(double arg0, Object arg1, double arg2, Object arg3);}
static public interface DODOD{double invokePrim(double arg0, Object arg1, double arg2, Object arg3);}
static public interface DODLO{Object invokePrim(double arg0, Object arg1, double arg2, long arg3);}
static public interface DODLL{long invokePrim(double arg0, Object arg1, double arg2, long arg3);}
static public interface DODLD{double invokePrim(double arg0, Object arg1, double arg2, long arg3);}
static public interface DODDO{Object invokePrim(double arg0, Object arg1, double arg2, double arg3);}
static public interface DODDL{long invokePrim(double arg0, Object arg1, double arg2, double arg3);}
static public interface DODDD{double invokePrim(double arg0, Object arg1, double arg2, double arg3);}
static public interface DLOOO{Object invokePrim(double arg0, long arg1, Object arg2, Object arg3);}
static public interface DLOOL{long invokePrim(double arg0, long arg1, Object arg2, Object arg3);}
static public interface DLOOD{double invokePrim(double arg0, long arg1, Object arg2, Object arg3);}
static public interface DLOLO{Object invokePrim(double arg0, long arg1, Object arg2, long arg3);}
static public interface DLOLL{long invokePrim(double arg0, long arg1, Object arg2, long arg3);}
static public interface DLOLD{double invokePrim(double arg0, long arg1, Object arg2, long arg3);}
static public interface DLODO{Object invokePrim(double arg0, long arg1, Object arg2, double arg3);}
static public interface DLODL{long invokePrim(double arg0, long arg1, Object arg2, double arg3);}
static public interface DLODD{double invokePrim(double arg0, long arg1, Object arg2, double arg3);}
static public interface DLLOO{Object invokePrim(double arg0, long arg1, long arg2, Object arg3);}
static public interface DLLOL{long invokePrim(double arg0, long arg1, long arg2, Object arg3);}
static public interface DLLOD{double invokePrim(double arg0, long arg1, long arg2, Object arg3);}
static public interface DLLLO{Object invokePrim(double arg0, long arg1, long arg2, long arg3);}
static public interface DLLLL{long invokePrim(double arg0, long arg1, long arg2, long arg3);}
static public interface DLLLD{double invokePrim(double arg0, long arg1, long arg2, long arg3);}
static public interface DLLDO{Object invokePrim(double arg0, long arg1, long arg2, double arg3);}
static public interface DLLDL{long invokePrim(double arg0, long arg1, long arg2, double arg3);}
static public interface DLLDD{double invokePrim(double arg0, long arg1, long arg2, double arg3);}
static public interface DLDOO{Object invokePrim(double arg0, long arg1, double arg2, Object arg3);}
static public interface DLDOL{long invokePrim(double arg0, long arg1, double arg2, Object arg3);}
static public interface DLDOD{double invokePrim(double arg0, long arg1, double arg2, Object arg3);}
static public interface DLDLO{Object invokePrim(double arg0, long arg1, double arg2, long arg3);}
static public interface DLDLL{long invokePrim(double arg0, long arg1, double arg2, long arg3);}
static public interface DLDLD{double invokePrim(double arg0, long arg1, double arg2, long arg3);}
static public interface DLDDO{Object invokePrim(double arg0, long arg1, double arg2, double arg3);}
static public interface DLDDL{long invokePrim(double arg0, long arg1, double arg2, double arg3);}
static public interface DLDDD{double invokePrim(double arg0, long arg1, double arg2, double arg3);}
static public interface DDOOO{Object invokePrim(double arg0, double arg1, Object arg2, Object arg3);}
static public interface DDOOL{long invokePrim(double arg0, double arg1, Object arg2, Object arg3);}
static public interface DDOOD{double invokePrim(double arg0, double arg1, Object arg2, Object arg3);}
static public interface DDOLO{Object invokePrim(double arg0, double arg1, Object arg2, long arg3);}
static public interface DDOLL{long invokePrim(double arg0, double arg1, Object arg2, long arg3);}
static public interface DDOLD{double invokePrim(double arg0, double arg1, Object arg2, long arg3);}
static public interface DDODO{Object invokePrim(double arg0, double arg1, Object arg2, double arg3);}
static public interface DDODL{long invokePrim(double arg0, double arg1, Object arg2, double arg3);}
static public interface DDODD{double invokePrim(double arg0, double arg1, Object arg2, double arg3);}
static public interface DDLOO{Object invokePrim(double arg0, double arg1, long arg2, Object arg3);}
static public interface DDLOL{long invokePrim(double arg0, double arg1, long arg2, Object arg3);}
static public interface DDLOD{double invokePrim(double arg0, double arg1, long arg2, Object arg3);}
static public interface DDLLO{Object invokePrim(double arg0, double arg1, long arg2, long arg3);}
static public interface DDLLL{long invokePrim(double arg0, double arg1, long arg2, long arg3);}
static public interface DDLLD{double invokePrim(double arg0, double arg1, long arg2, long arg3);}
static public interface DDLDO{Object invokePrim(double arg0, double arg1, long arg2, double arg3);}
static public interface DDLDL{long invokePrim(double arg0, double arg1, long arg2, double arg3);}
static public interface DDLDD{double invokePrim(double arg0, double arg1, long arg2, double arg3);}
static public interface DDDOO{Object invokePrim(double arg0, double arg1, double arg2, Object arg3);}
static public interface DDDOL{long invokePrim(double arg0, double arg1, double arg2, Object arg3);}
static public interface DDDOD{double invokePrim(double arg0, double arg1, double arg2, Object arg3);}
static public interface DDDLO{Object invokePrim(double arg0, double arg1, double arg2, long arg3);}
static public interface DDDLL{long invokePrim(double arg0, double arg1, double arg2, long arg3);}
static public interface DDDLD{double invokePrim(double arg0, double arg1, double arg2, long arg3);}
static public interface DDDDO{Object invokePrim(double arg0, double arg1, double arg2, double arg3);}
static public interface DDDDL{long invokePrim(double arg0, double arg1, double arg2, double arg3);}
static public interface DDDDD{double invokePrim(double arg0, double arg1, double arg2, double arg3);}
}
================================================
FILE: src/jvm/clojure/lang/IHashEq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich 10/23/11 */
package clojure.lang;
public interface IHashEq{
int hasheq();
}
================================================
FILE: src/jvm/clojure/lang/IKeywordLookup.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Oct 31, 2009 */
package clojure.lang;
public interface IKeywordLookup{
ILookupThunk getLookupThunk(Keyword k);
}
================================================
FILE: src/jvm/clojure/lang/ILookup.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Aug 2, 2009 */
package clojure.lang;
public interface ILookup{
Object valAt(Object key);
Object valAt(Object key, Object notFound);
}
================================================
FILE: src/jvm/clojure/lang/ILookupSite.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 2, 2009 */
package clojure.lang;
public interface ILookupSite{
ILookupThunk fault(Object target);
}
================================================
FILE: src/jvm/clojure/lang/ILookupThunk.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 2, 2009 */
package clojure.lang;
public interface ILookupThunk{
Object get(Object target);
}
================================================
FILE: src/jvm/clojure/lang/IMapEntry.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
import java.util.Map;
public interface IMapEntry extends Map.Entry{
Object key();
Object val();
}
================================================
FILE: src/jvm/clojure/lang/IMapIterable.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* Alex Miller Dec 3, 2014 */
package clojure.lang;
import java.util.Iterator;
/**
* Indicate a map can provide more efficient key and val iterators.
*/
public interface IMapIterable {
Iterator keyIterator();
Iterator valIterator();
}
================================================
FILE: src/jvm/clojure/lang/IMeta.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 31, 2008 */
package clojure.lang;
public interface IMeta {
IPersistentMap meta();
}
================================================
FILE: src/jvm/clojure/lang/IObj.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public interface IObj extends IMeta {
public IObj withMeta(IPersistentMap meta);
}
================================================
FILE: src/jvm/clojure/lang/IPending.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public interface IPending{
boolean isRealized();
}
================================================
FILE: src/jvm/clojure/lang/IPersistentCollection.java
================================================
package clojure.lang;
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
public interface IPersistentCollection extends Seqable {
int count();
IPersistentCollection cons(Object o);
IPersistentCollection empty();
boolean equiv(Object o);
}
================================================
FILE: src/jvm/clojure/lang/IPersistentList.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
public interface IPersistentList extends Sequential, IPersistentStack{
}
================================================
FILE: src/jvm/clojure/lang/IPersistentMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
public interface IPersistentMap extends Iterable, Associative, Counted{
IPersistentMap assoc(Object key, Object val);
IPersistentMap assocEx(Object key, Object val) ;
IPersistentMap without(Object key) ;
}
================================================
FILE: src/jvm/clojure/lang/IPersistentSet.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
public interface IPersistentSet extends IPersistentCollection, Counted{
public IPersistentSet disjoin(Object key) ;
public boolean contains(Object key);
public Object get(Object key);
}
================================================
FILE: src/jvm/clojure/lang/IPersistentStack.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Sep 19, 2007 */
package clojure.lang;
public interface IPersistentStack extends IPersistentCollection{
Object peek();
IPersistentStack pop();
}
================================================
FILE: src/jvm/clojure/lang/IPersistentVector.java
================================================
package clojure.lang;
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
public interface IPersistentVector extends Associative, Sequential, IPersistentStack, Reversible, Indexed{
int length();
IPersistentVector assocN(int i, Object val);
IPersistentVector cons(Object o);
}
================================================
FILE: src/jvm/clojure/lang/IProxy.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Feb 27, 2008 */
package clojure.lang;
public interface IProxy{
public void __initClojureFnMappings(IPersistentMap m);
public void __updateClojureFnMappings(IPersistentMap m);
public IPersistentMap __getClojureFnMappings();
}
================================================
FILE: src/jvm/clojure/lang/IRecord.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public interface IRecord {
}
================================================
FILE: src/jvm/clojure/lang/IReduce.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jun 11, 2008 */
package clojure.lang;
public interface IReduce extends IReduceInit{
Object reduce(IFn f) ;
}
================================================
FILE: src/jvm/clojure/lang/IReduceInit.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public interface IReduceInit{
Object reduce(IFn f, Object start) ;
}
================================================
FILE: src/jvm/clojure/lang/IRef.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 18, 2007 */
package clojure.lang;
public interface IRef extends IDeref{
void setValidator(IFn vf);
IFn getValidator();
IPersistentMap getWatches();
IRef addWatch(Object key, IFn callback);
IRef removeWatch(Object key);
}
================================================
FILE: src/jvm/clojure/lang/IReference.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 31, 2008 */
package clojure.lang;
public interface IReference extends IMeta {
IPersistentMap alterMeta(IFn alter, ISeq args) ;
IPersistentMap resetMeta(IPersistentMap m);
}
================================================
FILE: src/jvm/clojure/lang/ISeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
/**
* A persistent, functional, sequence interface
*
* ISeqs are immutable values, i.e. neither first(), nor rest() changes
* or invalidates the ISeq
*/
public interface ISeq extends IPersistentCollection {
Object first();
ISeq next();
ISeq more();
ISeq cons(Object o);
}
================================================
FILE: src/jvm/clojure/lang/ITransientAssociative.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 17, 2009 */
package clojure.lang;
public interface ITransientAssociative extends ITransientCollection, ILookup{
ITransientAssociative assoc(Object key, Object val);
}
================================================
FILE: src/jvm/clojure/lang/ITransientCollection.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 17, 2009 */
package clojure.lang;
public interface ITransientCollection{
ITransientCollection conj(Object val);
IPersistentCollection persistent();
}
================================================
FILE: src/jvm/clojure/lang/ITransientMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 17, 2009 */
package clojure.lang;
public interface ITransientMap extends ITransientAssociative, Counted{
ITransientMap assoc(Object key, Object val);
ITransientMap without(Object key);
IPersistentMap persistent();
}
================================================
FILE: src/jvm/clojure/lang/ITransientSet.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
public interface ITransientSet extends ITransientCollection, Counted{
public ITransientSet disjoin(Object key) ;
public boolean contains(Object key);
public Object get(Object key);
}
================================================
FILE: src/jvm/clojure/lang/ITransientVector.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 17, 2009 */
package clojure.lang;
public interface ITransientVector extends ITransientAssociative, Indexed{
ITransientVector assocN(int i, Object val);
ITransientVector pop();
}
================================================
FILE: src/jvm/clojure/lang/IType.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public interface IType {
}
================================================
FILE: src/jvm/clojure/lang/IllegalAccessError.java
================================================
package clojure.lang;
import java.lang.RuntimeException;
import java.lang.String;
public class IllegalAccessError extends RuntimeException {
public IllegalAccessError(String msg) {
super(msg);
}
}
================================================
FILE: src/jvm/clojure/lang/Indexed.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich May 24, 2009 */
package clojure.lang;
public interface Indexed extends Counted{
Object nth(int i);
Object nth(int i, Object notFound);
}
================================================
FILE: src/jvm/clojure/lang/IndexedSeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
public interface IndexedSeq extends ISeq, Sequential, Counted{
public int index();
}
================================================
FILE: src/jvm/clojure/lang/Intrinsics.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich 9/5/11 */
package clojure.lang;
import clojure.asm.Opcodes;
public class Intrinsics implements Opcodes{
private static Object[] oa(Object... arr){
return arr;
}
static IPersistentMap ops = RT.map(
"public static double clojure.lang.Numbers.add(double,double)", DADD,
"public static long clojure.lang.Numbers.and(long,long)", LAND,
"public static long clojure.lang.Numbers.or(long,long)", LOR,
"public static long clojure.lang.Numbers.xor(long,long)", LXOR,
"public static double clojure.lang.Numbers.multiply(double,double)", DMUL,
"public static double clojure.lang.Numbers.divide(double,double)", DDIV,
"public static long clojure.lang.Numbers.remainder(long,long)", LREM,
"public static long clojure.lang.Numbers.shiftLeft(long,long)", oa(L2I, LSHL),
"public static long clojure.lang.Numbers.shiftRight(long,long)", oa(L2I, LSHR),
"public static long clojure.lang.Numbers.unsignedShiftRight(long,long)", oa(L2I, LUSHR),
"public static double clojure.lang.Numbers.minus(double)", DNEG,
"public static double clojure.lang.Numbers.minus(double,double)", DSUB,
"public static double clojure.lang.Numbers.inc(double)", oa(DCONST_1, DADD),
"public static double clojure.lang.Numbers.dec(double)", oa(DCONST_1, DSUB),
"public static long clojure.lang.Numbers.quotient(long,long)", LDIV,
"public static int clojure.lang.Numbers.shiftLeftInt(int,int)", ISHL,
"public static int clojure.lang.Numbers.shiftRightInt(int,int)", ISHR,
"public static int clojure.lang.Numbers.unsignedShiftRightInt(int,int)", IUSHR,
"public static int clojure.lang.Numbers.unchecked_int_add(int,int)", IADD,
"public static int clojure.lang.Numbers.unchecked_int_subtract(int,int)", ISUB,
"public static int clojure.lang.Numbers.unchecked_int_negate(int)", INEG,
"public static int clojure.lang.Numbers.unchecked_int_inc(int)", oa(ICONST_1, IADD),
"public static int clojure.lang.Numbers.unchecked_int_dec(int)", oa(ICONST_1, ISUB),
"public static int clojure.lang.Numbers.unchecked_int_multiply(int,int)", IMUL,
"public static int clojure.lang.Numbers.unchecked_int_divide(int,int)", IDIV,
"public static int clojure.lang.Numbers.unchecked_int_remainder(int,int)", IREM,
"public static long clojure.lang.Numbers.unchecked_add(long,long)", LADD,
"public static double clojure.lang.Numbers.unchecked_add(double,double)", DADD,
"public static long clojure.lang.Numbers.unchecked_minus(long)", LNEG,
"public static double clojure.lang.Numbers.unchecked_minus(double)", DNEG,
"public static double clojure.lang.Numbers.unchecked_minus(double,double)", DSUB,
"public static long clojure.lang.Numbers.unchecked_minus(long,long)", LSUB,
"public static long clojure.lang.Numbers.unchecked_multiply(long,long)", LMUL,
"public static double clojure.lang.Numbers.unchecked_multiply(double,double)", DMUL,
"public static double clojure.lang.Numbers.unchecked_inc(double)", oa(DCONST_1, DADD),
"public static long clojure.lang.Numbers.unchecked_inc(long)", oa(LCONST_1, LADD),
"public static double clojure.lang.Numbers.unchecked_dec(double)", oa(DCONST_1, DSUB),
"public static long clojure.lang.Numbers.unchecked_dec(long)", oa(LCONST_1, LSUB),
"public static short clojure.lang.RT.aget(short[],int)", SALOAD,
"public static float clojure.lang.RT.aget(float[],int)", FALOAD,
"public static double clojure.lang.RT.aget(double[],int)", DALOAD,
"public static int clojure.lang.RT.aget(int[],int)", IALOAD,
"public static long clojure.lang.RT.aget(long[],int)", LALOAD,
"public static char clojure.lang.RT.aget(char[],int)", CALOAD,
"public static byte clojure.lang.RT.aget(byte[],int)", BALOAD,
"public static boolean clojure.lang.RT.aget(boolean[],int)", BALOAD,
"public static java.lang.Object clojure.lang.RT.aget(java.lang.Object[],int)", AALOAD,
"public static int clojure.lang.RT.alength(int[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(long[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(char[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(java.lang.Object[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(byte[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(float[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(short[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(boolean[])", ARRAYLENGTH,
"public static int clojure.lang.RT.alength(double[])", ARRAYLENGTH,
"public static double clojure.lang.RT.doubleCast(long)", L2D,
"public static double clojure.lang.RT.doubleCast(double)", NOP,
"public static double clojure.lang.RT.doubleCast(float)", F2D,
"public static double clojure.lang.RT.doubleCast(int)", I2D,
"public static double clojure.lang.RT.doubleCast(short)", I2D,
"public static double clojure.lang.RT.doubleCast(byte)", I2D,
"public static double clojure.lang.RT.uncheckedDoubleCast(double)", NOP,
"public static double clojure.lang.RT.uncheckedDoubleCast(float)", F2D,
"public static double clojure.lang.RT.uncheckedDoubleCast(long)", L2D,
"public static double clojure.lang.RT.uncheckedDoubleCast(int)", I2D,
"public static double clojure.lang.RT.uncheckedDoubleCast(short)", I2D,
"public static double clojure.lang.RT.uncheckedDoubleCast(byte)", I2D,
"public static long clojure.lang.RT.longCast(long)", NOP,
"public static long clojure.lang.RT.longCast(short)", I2L,
"public static long clojure.lang.RT.longCast(byte)", I2L,
"public static long clojure.lang.RT.longCast(int)", I2L,
"public static int clojure.lang.RT.uncheckedIntCast(long)", L2I,
"public static int clojure.lang.RT.uncheckedIntCast(double)", D2I,
"public static int clojure.lang.RT.uncheckedIntCast(byte)", NOP,
"public static int clojure.lang.RT.uncheckedIntCast(short)", NOP,
"public static int clojure.lang.RT.uncheckedIntCast(char)", NOP,
"public static int clojure.lang.RT.uncheckedIntCast(int)", NOP,
"public static int clojure.lang.RT.uncheckedIntCast(float)", F2I,
"public static long clojure.lang.RT.uncheckedLongCast(short)", I2L,
"public static long clojure.lang.RT.uncheckedLongCast(float)", F2L,
"public static long clojure.lang.RT.uncheckedLongCast(double)", D2L,
"public static long clojure.lang.RT.uncheckedLongCast(byte)", I2L,
"public static long clojure.lang.RT.uncheckedLongCast(long)", NOP,
"public static long clojure.lang.RT.uncheckedLongCast(int)", I2L
);
//map to instructions terminated with comparator for branch to false
static IPersistentMap preds = RT.map(
"public static boolean clojure.lang.Numbers.lt(double,double)", oa(DCMPG, IFGE),
"public static boolean clojure.lang.Numbers.lt(long,long)", oa(LCMP, IFGE),
"public static boolean clojure.lang.Numbers.equiv(double,double)", oa(DCMPL, IFNE),
"public static boolean clojure.lang.Numbers.equiv(long,long)", oa(LCMP, IFNE),
"public static boolean clojure.lang.Numbers.lte(double,double)", oa(DCMPG, IFGT),
"public static boolean clojure.lang.Numbers.lte(long,long)", oa(LCMP, IFGT),
"public static boolean clojure.lang.Numbers.gt(long,long)", oa(LCMP, IFLE),
"public static boolean clojure.lang.Numbers.gt(double,double)", oa(DCMPL, IFLE),
"public static boolean clojure.lang.Numbers.gte(long,long)", oa(LCMP, IFLT),
"public static boolean clojure.lang.Numbers.gte(double,double)", oa(DCMPL, IFLT),
"public static boolean clojure.lang.Util.equiv(long,long)", oa(LCMP, IFNE),
"public static boolean clojure.lang.Util.equiv(boolean,boolean)", oa(IF_ICMPNE),
"public static boolean clojure.lang.Util.equiv(double,double)", oa(DCMPL, IFNE),
"public static boolean clojure.lang.Numbers.isZero(double)", oa(DCONST_0, DCMPL, IFNE),
"public static boolean clojure.lang.Numbers.isZero(long)", oa(LCONST_0, LCMP, IFNE),
"public static boolean clojure.lang.Numbers.isPos(long)", oa(LCONST_0, LCMP, IFLE),
"public static boolean clojure.lang.Numbers.isPos(double)", oa(DCONST_0, DCMPL, IFLE),
"public static boolean clojure.lang.Numbers.isNeg(long)", oa(LCONST_0, LCMP, IFGE),
"public static boolean clojure.lang.Numbers.isNeg(double)", oa(DCONST_0, DCMPG, IFGE)
);
}
================================================
FILE: src/jvm/clojure/lang/Iterate.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
/* Alex Miller, Dec 5, 2014 */
public class Iterate extends ASeq implements IReduce, IPending {
private static final Object UNREALIZED_SEED = new Object();
private final IFn f; // never null
private final Object prevSeed;
private volatile Object _seed; // lazily realized
private volatile ISeq _next; // cached
private Iterate(IFn f, Object prevSeed, Object seed){
this.f = f;
this.prevSeed = prevSeed;
this._seed = seed;
}
private Iterate(IPersistentMap meta, IFn f, Object prevSeed, Object seed, ISeq next){
super(meta);
this.f = f;
this.prevSeed = prevSeed;
this._seed = seed;
this._next = next;
}
public static ISeq create(IFn f, Object seed){
return new Iterate(f, null, seed);
}
public boolean isRealized() {
return _seed != UNREALIZED_SEED;
}
public Object first(){
if(_seed == UNREALIZED_SEED) {
_seed = f.invoke(prevSeed);
}
return _seed;
}
public ISeq next(){
if(_next == null) {
_next = new Iterate(f, first(), UNREALIZED_SEED);
}
return _next;
}
public Iterate withMeta(IPersistentMap meta){
return new Iterate(meta, f, prevSeed, _seed, _next);
}
public Object reduce(IFn rf){
Object first = first();
Object ret = first;
Object v = f.invoke(first);
while(true){
ret = rf.invoke(ret, v);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
v = f.invoke(v);
}
}
public Object reduce(IFn rf, Object start){
Object ret = start;
Object v = first();
while(true){
ret = rf.invoke(ret, v);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
v = f.invoke(v);
}
}
}
================================================
FILE: src/jvm/clojure/lang/IteratorSeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.Iterator;
public class IteratorSeq extends ASeq{
final Iterator iter;
final State state;
static class State{
volatile Object val;
volatile Object _rest;
}
public static IteratorSeq create(Iterator iter){
if(iter.hasNext())
return new IteratorSeq(iter);
return null;
}
IteratorSeq(Iterator iter){
this.iter = iter;
state = new State();
this.state.val = state;
this.state._rest = state;
}
IteratorSeq(IPersistentMap meta, Iterator iter, State state){
super(meta);
this.iter = iter;
this.state = state;
}
public Object first(){
if(state.val == state)
synchronized(state)
{
if(state.val == state)
state.val = iter.next();
}
return state.val;
}
public ISeq next(){
if(state._rest == state)
synchronized(state)
{
if(state._rest == state)
{
first();
state._rest = create(iter);
}
}
return (ISeq) state._rest;
}
public IteratorSeq withMeta(IPersistentMap meta){
return new IteratorSeq(meta, iter, state);
}
private void writeObject (java.io.ObjectOutputStream out) throws IOException {
throw new NotSerializableException(getClass().getName());
}
}
================================================
FILE: src/jvm/clojure/lang/Keyword.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 29, 2006 10:39:05 AM */
package clojure.lang;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class Keyword implements IFn, Comparable, Named, Serializable, IHashEq {
private static ConcurrentHashMap> table = new ConcurrentHashMap();
static final ReferenceQueue rq = new ReferenceQueue();
public final Symbol sym;
final int hasheq;
transient String _str;
public static Keyword intern(Symbol sym){
Keyword k = null;
Reference existingRef = table.get(sym);
if(existingRef == null)
{
Util.clearCache(rq, table);
if(sym.meta() != null)
sym = (Symbol) sym.withMeta(null);
k = new Keyword(sym);
existingRef = table.putIfAbsent(sym, new WeakReference(k, rq));
}
if(existingRef == null)
return k;
Keyword existingk = existingRef.get();
if(existingk != null)
return existingk;
//entry died in the interim, do over
table.remove(sym, existingRef);
return intern(sym);
}
public static Keyword intern(String ns, String name){
return intern(Symbol.intern(ns, name));
}
public static Keyword intern(String nsname){
return intern(Symbol.intern(nsname));
}
private Keyword(Symbol sym){
this.sym = sym;
hasheq = sym.hasheq() + 0x9e3779b9;
}
public static Keyword find(Symbol sym){
Reference ref = table.get(sym);
if (ref != null)
return ref.get();
else
return null;
}
public static Keyword find(String ns, String name){
return find(Symbol.intern(ns, name));
}
public static Keyword find(String nsname){
return find(Symbol.intern(nsname));
}
public final int hashCode(){
return Util.hash(sym) + 0x9e3779b9;
}
public int hasheq() {
return hasheq;
}
public String toString(){
if(_str == null)
_str = (":" + sym);
return _str;
}
public Object throwArity(){
throw new IllegalArgumentException("Wrong number of args passed to keyword: "
+ toString());
}
public Object call() {
return throwArity();
}
public void run(){
throw new UnsupportedOperationException();
}
public Object invoke() {
return throwArity();
}
public int compareTo(Object o){
return sym.compareTo(((Keyword) o).sym);
}
public String getNamespace(){
return sym.getNamespace();
}
public String getName(){
return sym.getName();
}
private Object readResolve() throws ObjectStreamException{
return intern(sym);
}
/**
* Indexer implements IFn for attr access
*
* @param obj - must be IPersistentMap
* @return the value at the key or nil if not found
* @
*/
final public Object invoke(Object obj) {
if(obj instanceof ILookup)
return ((ILookup)obj).valAt(this);
return RT.get(obj, this);
}
final public Object invoke(Object obj, Object notFound) {
if(obj instanceof ILookup)
return ((ILookup)obj).valAt(this,notFound);
return RT.get(obj, this, notFound);
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
{
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
{
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
{
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
{
return throwArity();
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
Object... args)
{
return throwArity();
}
public Object applyTo(ISeq arglist) {
return AFn.applyToHelper(this, arglist);
}
}
================================================
FILE: src/jvm/clojure/lang/KeywordLookupSite.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 2, 2009 */
package clojure.lang;
public final class KeywordLookupSite implements ILookupSite, ILookupThunk{
final Keyword k;
public KeywordLookupSite(Keyword k){
this.k = k;
}
public ILookupThunk fault(Object target){
if(target instanceof IKeywordLookup)
{
return install(target);
}
else if(target instanceof ILookup)
{
return ilookupThunk(target.getClass());
}
return this;
}
public Object get(Object target){
if(target instanceof IKeywordLookup || target instanceof ILookup)
return this;
return RT.get(target,k);
}
private ILookupThunk ilookupThunk(final Class c){
return new ILookupThunk(){
public Object get(Object target){
if(target != null && target.getClass() == c)
return ((ILookup) target).valAt(k);
return this;
}
};
}
private ILookupThunk install(Object target){
ILookupThunk t = ((IKeywordLookup)target).getLookupThunk(k);
if(t != null)
return t;
return ilookupThunk(target.getClass());
}
}
================================================
FILE: src/jvm/clojure/lang/LazilyPersistentVector.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich May 14, 2008 */
package clojure.lang;
public class LazilyPersistentVector{
static public IPersistentVector createOwning(Object... items){
if(items.length == 0)
return PersistentVector.EMPTY;
else if(items.length <= 32)
return new PersistentVector(items.length, 5, PersistentVector.EMPTY_NODE,items);
return PersistentVector.create(items);
}
static public IPersistentVector create(Object obj){
if(obj instanceof IReduceInit)
return PersistentVector.create((IReduceInit) obj);
else if(obj instanceof ISeq)
return PersistentVector.create(RT.seq(obj));
else if(obj instanceof Iterable)
return PersistentVector.create((Iterable)obj);
else
return createOwning(RT.toArray(obj));
}
}
================================================
FILE: src/jvm/clojure/lang/LazySeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 31, 2009 */
package clojure.lang;
import java.util.*;
public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending, IHashEq{
private IFn fn;
private Object sv;
private ISeq s;
public LazySeq(IFn fn){
this.fn = fn;
}
private LazySeq(IPersistentMap meta, ISeq s){
super(meta);
this.fn = null;
this.s = s;
}
public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
final synchronized Object sval(){
if(fn != null)
{
sv = fn.invoke();
fn = null;
}
if(sv != null)
return sv;
return s;
}
final synchronized public ISeq seq(){
sval();
if(sv != null)
{
Object ls = sv;
sv = null;
while(ls instanceof LazySeq)
{
ls = ((LazySeq)ls).sval();
}
s = RT.seq(ls);
}
return s;
}
public int count(){
int c = 0;
for(ISeq s = seq(); s != null; s = s.next())
++c;
return c;
}
public Object first(){
seq();
if(s == null)
return null;
return s.first();
}
public ISeq next(){
seq();
if(s == null)
return null;
return s.next();
}
public ISeq more(){
seq();
if(s == null)
return PersistentList.EMPTY;
return s.more();
}
public ISeq cons(Object o){
return RT.cons(o, seq());
}
public IPersistentCollection empty(){
return PersistentList.EMPTY;
}
public boolean equiv(Object o){
ISeq s = seq();
if(s != null)
return s.equiv(o);
else
return (o instanceof Sequential || o instanceof List) && RT.seq(o) == null;
}
public int hashCode(){
ISeq s = seq();
if(s == null)
return 1;
return Util.hash(seq());
}
public int hasheq(){
return Murmur3.hashOrdered(this);
}
public boolean equals(Object o){
ISeq s = seq();
if(s != null)
return s.equals(o);
else
return (o instanceof Sequential || o instanceof List) && RT.seq(o) == null;
}
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
return RT.seqToPassedArray(seq(), a);
}
public int size(){
return count();
}
public boolean isEmpty(){
return seq() == null;
}
public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.next())
{
if(Util.equiv(s.first(), o))
return true;
}
return false;
}
public Iterator iterator(){
return new SeqIterator(this);
}
//////////// List stuff /////////////////
private List reify(){
return new ArrayList(this);
}
public List subList(int fromIndex, int toIndex){
return reify().subList(fromIndex, toIndex);
}
public Object set(int index, Object element){
throw new UnsupportedOperationException();
}
public Object remove(int index){
throw new UnsupportedOperationException();
}
public int indexOf(Object o){
ISeq s = seq();
for(int i = 0; s != null; s = s.next(), i++)
{
if(Util.equiv(s.first(), o))
return i;
}
return -1;
}
public int lastIndexOf(Object o){
return reify().lastIndexOf(o);
}
public ListIterator listIterator(){
return reify().listIterator();
}
public ListIterator listIterator(int index){
return reify().listIterator(index);
}
public Object get(int index){
return RT.nth(this, index);
}
public void add(int index, Object element){
throw new UnsupportedOperationException();
}
public boolean addAll(int index, Collection c){
throw new UnsupportedOperationException();
}
synchronized public boolean isRealized(){
return fn == null;
}
}
================================================
FILE: src/jvm/clojure/lang/LineNumberingPushbackReader.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
package clojure.lang;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.LineNumberReader;
import java.io.IOException;
public class LineNumberingPushbackReader extends PushbackReader{
// This class is a PushbackReader that wraps a LineNumberReader. The code
// here to handle line terminators only mentions '\n' because
// LineNumberReader collapses all occurrences of CR, LF, and CRLF into a
// single '\n'.
private static final int newline = (int) '\n';
private boolean _atLineStart = true;
private boolean _prev;
private int _columnNumber = 1;
public LineNumberingPushbackReader(Reader r){
super(new LineNumberReader(r));
}
public LineNumberingPushbackReader(Reader r, int size){
super(new LineNumberReader(r, size));
}
public int getLineNumber(){
return ((LineNumberReader) in).getLineNumber() + 1;
}
public void setLineNumber(int line) { ((LineNumberReader) in).setLineNumber(line - 1); }
public int getColumnNumber(){
return _columnNumber;
}
public int read() throws IOException{
int c = super.read();
_prev = _atLineStart;
if((c == newline) || (c == -1))
{
_atLineStart = true;
_columnNumber = 1;
}
else
{
_atLineStart = false;
_columnNumber++;
}
return c;
}
public void unread(int c) throws IOException{
super.unread(c);
_atLineStart = _prev;
_columnNumber--;
}
public String readLine() throws IOException{
int c = read();
String line;
switch (c) {
case -1:
line = null;
break;
case newline:
line = "";
break;
default:
String first = String.valueOf((char) c);
String rest = ((LineNumberReader)in).readLine();
line = (rest == null) ? first : first + rest;
_prev = false;
_atLineStart = true;
_columnNumber = 1;
break;
}
return line;
}
public boolean atLineStart(){
return _atLineStart;
}
}
================================================
FILE: src/jvm/clojure/lang/LispReader.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.lang.Character;
import java.lang.Class;
import java.lang.Exception;
import java.lang.IllegalArgumentException;
import java.lang.IllegalStateException;
import java.lang.Integer;
import java.lang.Number;
import java.lang.NumberFormatException;
import java.lang.Object;
import java.lang.RuntimeException;
import java.lang.String;
import java.lang.StringBuilder;
import java.lang.Throwable;
import java.lang.UnsupportedOperationException;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LispReader{
static final Symbol QUOTE = Symbol.intern("quote");
static final Symbol THE_VAR = Symbol.intern("var");
//static Symbol SYNTAX_QUOTE = Symbol.intern(null, "syntax-quote");
static Symbol UNQUOTE = Symbol.intern("clojure.core", "unquote");
static Symbol UNQUOTE_SPLICING = Symbol.intern("clojure.core", "unquote-splicing");
static Symbol CONCAT = Symbol.intern("clojure.core", "concat");
static Symbol SEQ = Symbol.intern("clojure.core", "seq");
static Symbol LIST = Symbol.intern("clojure.core", "list");
static Symbol APPLY = Symbol.intern("clojure.core", "apply");
static Symbol HASHMAP = Symbol.intern("clojure.core", "hash-map");
static Symbol HASHSET = Symbol.intern("clojure.core", "hash-set");
static Symbol VECTOR = Symbol.intern("clojure.core", "vector");
static Symbol WITH_META = Symbol.intern("clojure.core", "with-meta");
static Symbol META = Symbol.intern("clojure.core", "meta");
static Symbol DEREF = Symbol.intern("clojure.core", "deref");
static Symbol READ_COND = Symbol.intern("clojure.core", "read-cond");
static Symbol READ_COND_SPLICING = Symbol.intern("clojure.core", "read-cond-splicing");
static Keyword UNKNOWN = Keyword.intern(null, "unknown");
//static Symbol DEREF_BANG = Symbol.intern("clojure.core", "deref!");
static IFn[] macros = new IFn[256];
static IFn[] dispatchMacros = new IFn[256];
//static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^:/]][^:/]*/)?[\\D&&[^:/]][^:/]*");
static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
//static Pattern varPat = Pattern.compile("([\\D&&[^:\\.]][^:\\.]*):([\\D&&[^:\\.]][^:\\.]*)");
//static Pattern intPat = Pattern.compile("[-+]?[0-9]+\\.?");
static Pattern intPat =
Pattern.compile(
"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
//static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*");
//static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
//static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
//static Pattern classNamePat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.");
//symbol->gensymbol
static Var GENSYM_ENV = Var.create(null).setDynamic();
//sorted-map num->gensymbol
static Var ARG_ENV = Var.create(null).setDynamic();
static IFn ctorReader = new CtorReader();
// Dynamic var set to true in a read-cond context
static Var READ_COND_ENV = Var.create(null).setDynamic();
static
{
macros['"'] = new StringReader();
macros[';'] = new CommentReader();
macros['\''] = new WrappingReader(QUOTE);
macros['@'] = new WrappingReader(DEREF);//new DerefReader();
macros['^'] = new MetaReader();
macros['`'] = new SyntaxQuoteReader();
macros['~'] = new UnquoteReader();
macros['('] = new ListReader();
macros[')'] = new UnmatchedDelimiterReader();
macros['['] = new VectorReader();
macros[']'] = new UnmatchedDelimiterReader();
macros['{'] = new MapReader();
macros['}'] = new UnmatchedDelimiterReader();
// macros['|'] = new ArgVectorReader();
macros['\\'] = new CharacterReader();
macros['%'] = new ArgReader();
macros['#'] = new DispatchReader();
dispatchMacros['^'] = new MetaReader();
dispatchMacros['\''] = new VarReader();
dispatchMacros['"'] = new RegexReader();
dispatchMacros['('] = new FnReader();
dispatchMacros['{'] = new SetReader();
dispatchMacros['='] = new EvalReader();
dispatchMacros['!'] = new CommentReader();
dispatchMacros['<'] = new UnreadableReader();
dispatchMacros['_'] = new DiscardReader();
dispatchMacros['?'] = new ConditionalReader();
}
static boolean isWhitespace(int ch){
return Character.isWhitespace((char)ch) || ch == ',';
}
static void unread(PushbackReader r, int ch) {
if(ch != -1)
try
{
r.unread(ch);
}
catch(IOException e)
{
throw Util.sneakyThrow(e);
}
}
public static class ReaderException extends RuntimeException{
final int line;
final int column;
public ReaderException(int line, int column, Throwable cause){
super(cause);
this.line = line;
this.column = column;
}
}
static public int read1(Reader r){
try
{
return r.read();
}
catch(IOException e)
{
throw Util.sneakyThrow(e);
}
}
// Reader opts
static public final Keyword OPT_EOF = Keyword.intern(null,"eof");
static public final Keyword OPT_FEATURES = Keyword.intern(null,"features");
static public final Keyword OPT_READ_COND = Keyword.intern(null, "read-cond");
// EOF special value to throw on eof
static public final Keyword EOFTHROW = Keyword.intern(null,"eofthrow");
// Platform features - always installed
static private final Keyword PLATFORM_KEY = Keyword.intern(null, "clj");
static private final Object PLATFORM_FEATURES = PersistentHashSet.create(PLATFORM_KEY);
// Reader conditional options - use with :read-cond
static public final Keyword COND_ALLOW = Keyword.intern(null, "allow");
static public final Keyword COND_PRESERVE = Keyword.intern(null, "preserve");
static public Object read(PushbackReader r, Object opts){
boolean eofIsError = true;
Object eofValue = null;
if(opts != null && opts instanceof IPersistentMap)
{
Object eof = ((IPersistentMap)opts).valAt(OPT_EOF, EOFTHROW);
if(!EOFTHROW.equals(eof)) {
eofIsError = false;
eofValue = eof;
}
}
return read(r,eofIsError,eofValue,false,opts);
}
static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive)
{
return read(r, eofIsError, eofValue, isRecursive, PersistentHashMap.EMPTY);
}
static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts)
{
// start with pendingForms null as reader conditional splicing is not allowed at top level
return read(r, eofIsError, eofValue, null, null, isRecursive, opts, null);
}
static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts, Object pendingForms) {
return read(r, eofIsError, eofValue, null, null, isRecursive, opts, ensurePending(pendingForms));
}
static private Object ensurePending(Object pendingForms) {
if(pendingForms == null)
return new LinkedList();
else
return pendingForms;
}
static private Object installPlatformFeature(Object opts) {
if(opts == null)
return RT.mapUniqueKeys(LispReader.OPT_FEATURES, PLATFORM_FEATURES);
else {
IPersistentMap mopts = (IPersistentMap) opts;
Object features = mopts.valAt(OPT_FEATURES);
if (features == null)
return mopts.assoc(LispReader.OPT_FEATURES, PLATFORM_FEATURES);
else
return mopts.assoc(LispReader.OPT_FEATURES, RT.conj((IPersistentSet) features, PLATFORM_KEY));
}
}
static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, Character returnOn, Object returnOnValue, boolean isRecursive, Object opts, Object pendingForms)
{
if(RT.READEVAL.deref() == UNKNOWN)
throw Util.runtimeException("Reading disallowed - *read-eval* bound to :unknown");
opts = installPlatformFeature(opts);
try
{
for(; ;)
{
if(pendingForms instanceof List && !((List)pendingForms).isEmpty())
return ((List)pendingForms).remove(0);
int ch = read1(r);
while(isWhitespace(ch))
ch = read1(r);
if(ch == -1)
{
if(eofIsError)
throw Util.runtimeException("EOF while reading");
return eofValue;
}
if(returnOn != null && (returnOn.charValue() == ch)) {
return returnOnValue;
}
if(Character.isDigit((char)ch))
{
Object n = readNumber(r, (char) ch);
return n;
}
IFn macroFn = getMacro(ch);
if(macroFn != null)
{
Object ret = macroFn.invoke(r, (char) ch, opts, pendingForms);
//no op macros return the reader
if(ret == r)
continue;
return ret;
}
if(ch == '+' || ch == '-')
{
int ch2 = read1(r);
if(Character.isDigit((char)ch2))
{
unread(r, ch2);
Object n = readNumber(r, (char) ch);
return n;
}
unread(r, ch2);
}
String token = readToken(r, (char) ch);
return interpretToken(token);
}
}
catch(Exception e)
{
if(isRecursive || !(r instanceof LineNumberingPushbackReader))
throw Util.sneakyThrow(e);
LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
//throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
}
}
static private String readToken(PushbackReader r, char initch) {
StringBuilder sb = new StringBuilder();
sb.append(initch);
for(; ;)
{
int ch = read1(r);
if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
{
unread(r, ch);
return sb.toString();
}
sb.append((char) ch);
}
}
static private Object readNumber(PushbackReader r, char initch) {
StringBuilder sb = new StringBuilder();
sb.append(initch);
for(; ;)
{
int ch = read1(r);
if(ch == -1 || isWhitespace(ch) || isMacro(ch))
{
unread(r, ch);
break;
}
sb.append((char) ch);
}
String s = sb.toString();
Object n = matchNumber(s);
if(n == null)
throw new NumberFormatException("Invalid number: " + s);
return n;
}
static private int readUnicodeChar(String token, int offset, int length, int base) {
if(token.length() != offset + length)
throw new IllegalArgumentException("Invalid unicode character: \\" + token);
int uc = 0;
for(int i = offset; i < offset + length; ++i)
{
int d = Character.digit(token.charAt(i), base);
if(d == -1)
throw new IllegalArgumentException("Invalid digit: " + token.charAt(i));
uc = uc * base + d;
}
return (char) uc;
}
static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) {
int uc = Character.digit((char)initch, base);
if(uc == -1)
throw new IllegalArgumentException("Invalid digit: " + (char) initch);
int i = 1;
for(; i < length; ++i)
{
int ch = read1(r);
if(ch == -1 || isWhitespace(ch) || isMacro(ch))
{
unread(r, ch);
break;
}
int d = Character.digit((char)ch, base);
if(d == -1)
throw new IllegalArgumentException("Invalid digit: " + (char) ch);
uc = uc * base + d;
}
if(i != length && exact)
throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length);
return uc;
}
static private Object interpretToken(String s) {
if(s.equals("nil"))
{
return null;
}
else if(s.equals("true"))
{
return RT.T;
}
else if(s.equals("false"))
{
return RT.F;
}
Object ret = null;
ret = matchSymbol(s);
if(ret != null)
return ret;
throw Util.runtimeException("Invalid token: " + s);
}
private static Object matchSymbol(String s){
Matcher m = symbolPat.matcher(s);
if(m.matches())
{
int gc = m.groupCount();
String ns = m.group(1);
String name = m.group(2);
if(ns != null && ns.endsWith(":/")
|| name.endsWith(":")
|| s.indexOf("::", 1) != -1)
return null;
if(s.startsWith("::"))
{
Symbol ks = Symbol.intern(s.substring(2));
Namespace kns;
if(ks.ns != null)
kns = Compiler.namespaceFor(ks);
else
kns = Compiler.currentNS();
//auto-resolving keyword
if (kns != null)
return Keyword.intern(kns.name.name,ks.name);
else
return null;
}
boolean isKeyword = s.charAt(0) == ':';
Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
if(isKeyword)
return Keyword.intern(sym);
return sym;
}
return null;
}
private static Object matchNumber(String s){
if (ObjC.objc) {
Object r = nativeMatchNumber(s);
if (r != null) {
return r;
}
}
Matcher m = intPat.matcher(s);
if(m.matches())
{
if(m.group(2) != null)
{
if(m.group(8) != null)
return BigInt.ZERO;
return Numbers.num(0);
}
boolean negate = (m.group(1).equals("-"));
String n;
int radix = 10;
if((n = m.group(3)) != null)
radix = 10;
else if((n = m.group(4)) != null)
radix = 16;
else if((n = m.group(5)) != null)
radix = 8;
else if((n = m.group(7)) != null)
radix = Integer.parseInt(m.group(6));
if(n == null)
return null;
BigInteger bn = new BigInteger(n, radix);
if(negate)
bn = bn.negate();
if(m.group(8) != null)
return BigInt.fromBigInteger(bn);
return bn.bitLength() < 64 ?
Numbers.num(bn.longValue())
: BigInt.fromBigInteger(bn);
}
m = floatPat.matcher(s);
if(m.matches())
{
if(m.group(4) != null)
return new BigDecimal(m.group(1));
return Double.parseDouble(s);
}
m = ratioPat.matcher(s);
if(m.matches())
{
String numerator = m.group(1);
if (numerator.startsWith("+")) numerator = numerator.substring(1);
return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(numerator))),
Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(2)))));
}
return null;
}
private native static Object nativeMatchNumber(String s) /*-[
long long v;
NSScanner *scanner = [NSScanner scannerWithString:s];
if ([scanner scanLongLong:&v] && scanner.scanLocation == s.length) {
return [ClojureLangRT boxWithLong:v];
}
double d;
scanner = [NSScanner scannerWithString:s];
if ([scanner scanDouble:&d] && scanner.scanLocation == s.length) {
return [ClojureLangRT boxWithDouble:d];
}
return nil;
]-*/;
static private IFn getMacro(int ch){
if(ch < macros.length)
return macros[ch];
return null;
}
static private boolean isMacro(int ch){
return (ch < macros.length && macros[ch] != null);
}
static private boolean isTerminatingMacro(int ch){
return (ch != '#' && ch != '\'' && ch != '%' && isMacro(ch));
}
public static class RegexReader extends AFn{
static StringReader stringrdr = new StringReader();
public Object invoke(Object reader, Object doublequote, Object opts, Object pendingForms) {
StringBuilder sb = new StringBuilder();
Reader r = (Reader) reader;
for(int ch = read1(r); ch != '"'; ch = read1(r))
{
if(ch == -1)
throw Util.runtimeException("EOF while reading regex");
sb.append( (char) ch );
if(ch == '\\') //escape
{
ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading regex");
sb.append( (char) ch ) ;
}
}
return Pattern.compile(sb.toString());
}
}
public static class StringReader extends AFn{
public Object invoke(Object reader, Object doublequote, Object opts, Object pendingForms) {
StringBuilder sb = new StringBuilder();
Reader r = (Reader) reader;
for(int ch = read1(r); ch != '"'; ch = read1(r))
{
if(ch == -1)
throw Util.runtimeException("EOF while reading string");
if(ch == '\\') //escape
{
ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading string");
switch(ch)
{
case 't':
ch = '\t';
break;
case 'r':
ch = '\r';
break;
case 'n':
ch = '\n';
break;
case '\\':
break;
case '"':
break;
case 'b':
ch = '\b';
break;
case 'f':
ch = '\f';
break;
case 'u':
{
ch = read1(r);
if (Character.digit((char)ch, 16) == -1)
throw Util.runtimeException("Invalid unicode escape: \\u" + (char) ch);
ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
break;
}
default:
{
if(Character.isDigit((char)ch))
{
ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false);
if(ch > 0377)
throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
}
else
throw Util.runtimeException("Unsupported escape character: \\" + (char) ch);
}
}
}
sb.append((char) ch);
}
return sb.toString();
}
}
public static class CommentReader extends AFn{
public Object invoke(Object reader, Object semicolon, Object opts, Object pendingForms) {
Reader r = (Reader) reader;
int ch;
do
{
ch = read1(r);
} while(ch != -1 && ch != '\n' && ch != '\r');
return r;
}
}
public static class DiscardReader extends AFn{
public Object invoke(Object reader, Object underscore, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
read(r, true, null, true, opts, ensurePending(pendingForms));
return r;
}
}
public static class WrappingReader extends AFn{
final Symbol sym;
public WrappingReader(Symbol sym){
this.sym = sym;
}
public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
return RT.list(sym, o);
}
}
public static class DeprecatedWrappingReader extends AFn{
final Symbol sym;
final String macro;
public DeprecatedWrappingReader(Symbol sym, String macro){
this.sym = sym;
this.macro = macro;
}
public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
System.out.println("WARNING: reader macro " + macro +
" is deprecated; use " + sym.getName() +
" instead");
PushbackReader r = (PushbackReader) reader;
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
return RT.list(sym, o);
}
}
public static class VarReader extends AFn{
public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
// if(o instanceof Symbol)
// {
// Object v = Compiler.maybeResolveIn(Compiler.currentNS(), (Symbol) o);
// if(v instanceof Var)
// return v;
// }
return RT.list(THE_VAR, o);
}
}
/*
static class DerefReader extends AFn{
public Object invoke(Object reader, Object quote) {
PushbackReader r = (PushbackReader) reader;
int ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading character");
if(ch == '!')
{
Object o = read(r, true, null, true);
return RT.list(DEREF_BANG, o);
}
else
{
r.unread(ch);
Object o = read(r, true, null, true);
return RT.list(DEREF, o);
}
}
}
*/
public static class DispatchReader extends AFn{
public Object invoke(Object reader, Object hash, Object opts, Object pendingForms) {
int ch = read1((Reader) reader);
if(ch == -1)
throw Util.runtimeException("EOF while reading character");
IFn fn = dispatchMacros[ch];
// Try the ctor reader first
if(fn == null) {
unread((PushbackReader) reader, ch);
pendingForms = ensurePending(pendingForms);
Object result = ctorReader.invoke(reader, ch, opts, pendingForms);
if(result != null)
return result;
else
throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
}
return fn.invoke(reader, ch, opts, pendingForms);
}
}
static Symbol garg(int n){
return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID() + "#");
}
public static class FnReader extends AFn{
public Object invoke(Object reader, Object lparen, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
if(ARG_ENV.deref() != null)
throw new IllegalStateException("Nested #()s are not allowed");
try
{
Var.pushThreadBindings(
RT.map(ARG_ENV, PersistentTreeMap.EMPTY));
unread(r, '(');
Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
PersistentVector args = PersistentVector.EMPTY;
PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref();
ISeq rargs = argsyms.rseq();
if(rargs != null)
{
int higharg = (Integer) ((Map.Entry) rargs.first()).getKey();
if(higharg > 0)
{
for(int i = 1; i <= higharg; ++i)
{
Object sym = argsyms.valAt(i);
if(sym == null)
sym = garg(i);
args = args.cons(sym);
}
}
Object restsym = argsyms.valAt(-1);
if(restsym != null)
{
args = args.cons(Compiler._AMP_);
args = args.cons(restsym);
}
}
return RT.list(Compiler.FN, args, form);
}
finally
{
Var.popThreadBindings();
}
}
}
static Symbol registerArg(int n){
PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref();
if(argsyms == null)
{
throw new IllegalStateException("arg literal not in #()");
}
Symbol ret = (Symbol) argsyms.valAt(n);
if(ret == null)
{
ret = garg(n);
ARG_ENV.set(argsyms.assoc(n, ret));
}
return ret;
}
static class ArgReader extends AFn{
public Object invoke(Object reader, Object pct, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
if(ARG_ENV.deref() == null)
{
return interpretToken(readToken(r, '%'));
}
int ch = read1(r);
unread(r, ch);
//% alone is first arg
if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
{
return registerArg(1);
}
Object n = read(r, true, null, true, opts, ensurePending(pendingForms));
if(n.equals(Compiler._AMP_))
return registerArg(-1);
if(!(n instanceof Number))
throw new IllegalStateException("arg literal must be %, %& or %integer");
return registerArg(((Number) n).intValue());
}
}
public static class MetaReader extends AFn{
public Object invoke(Object reader, Object caret, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
int line = -1;
int column = -1;
if(r instanceof LineNumberingPushbackReader)
{
line = ((LineNumberingPushbackReader) r).getLineNumber();
column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
}
pendingForms = ensurePending(pendingForms);
Object meta = read(r, true, null, true, opts, pendingForms);
if(meta instanceof Symbol || meta instanceof String)
meta = RT.map(RT.TAG_KEY, meta);
else if (meta instanceof Keyword)
meta = RT.map(meta, RT.T);
else if(!(meta instanceof IPersistentMap))
throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
Object o = read(r, true, null, true, opts, pendingForms);
if(o instanceof IMeta)
{
if(line != -1 && o instanceof ISeq)
{
meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line).assoc(RT.COLUMN_KEY, column);
}
if(o instanceof IReference)
{
((IReference)o).resetMeta((IPersistentMap) meta);
return o;
}
Object ometa = RT.meta(o);
for(ISeq s = RT.seq(meta); s != null; s = s.next()) {
IMapEntry kv = (IMapEntry) s.first();
ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
}
return ((IObj) o).withMeta((IPersistentMap) ometa);
}
else
throw new IllegalArgumentException("Metadata can only be applied to IMetas");
}
}
public static class SyntaxQuoteReader extends AFn{
public Object invoke(Object reader, Object backquote, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
try
{
Var.pushThreadBindings(
RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
return syntaxQuote(form);
}
finally
{
Var.popThreadBindings();
}
}
static Object syntaxQuote(Object form) {
Object ret;
if(Compiler.isSpecial(form))
ret = RT.list(Compiler.QUOTE, form);
else if(form instanceof Symbol)
{
Symbol sym = (Symbol) form;
if(sym.ns == null && sym.name.endsWith("#"))
{
IPersistentMap gmap = (IPersistentMap) GENSYM_ENV.deref();
if(gmap == null)
throw new IllegalStateException("Gensym literal not in syntax-quote");
Symbol gs = (Symbol) gmap.valAt(sym);
if(gs == null)
GENSYM_ENV.set(gmap.assoc(sym, gs = Symbol.intern(null,
sym.name.substring(0, sym.name.length() - 1)
+ "__" + RT.nextID() + "__auto__")));
sym = gs;
}
else if(sym.ns == null && sym.name.endsWith("."))
{
Symbol csym = Symbol.intern(null, sym.name.substring(0, sym.name.length() - 1));
csym = Compiler.resolveSymbol(csym);
sym = Symbol.intern(null, csym.name.concat("."));
}
else if(sym.ns == null && sym.name.startsWith("."))
{
// Simply quote method names.
}
else
{
Object maybeClass = null;
if(sym.ns != null)
maybeClass = Compiler.currentNS().getMapping(
Symbol.intern(null, sym.ns));
if(maybeClass instanceof Class)
{
// Classname/foo -> package.qualified.Classname/foo
sym = Symbol.intern(
((Class)maybeClass).getName(), sym.name);
}
else
sym = Compiler.resolveSymbol(sym);
}
ret = RT.list(Compiler.QUOTE, sym);
}
else if(isUnquote(form))
return RT.second(form);
else if(isUnquoteSplicing(form))
throw new IllegalStateException("splice not in list");
else if(form instanceof IPersistentCollection)
{
if(form instanceof IRecord)
ret = form;
else if(form instanceof IPersistentMap)
{
IPersistentVector keyvals = flattenMap(form);
ret = RT.list(APPLY, HASHMAP, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(keyvals.seq()))));
}
else if(form instanceof IPersistentVector)
{
ret = RT.list(APPLY, VECTOR, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentVector) form).seq()))));
}
else if(form instanceof IPersistentSet)
{
ret = RT.list(APPLY, HASHSET, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentSet) form).seq()))));
}
else if(form instanceof ISeq || form instanceof IPersistentList)
{
ISeq seq = RT.seq(form);
if(seq == null)
ret = RT.cons(LIST,null);
else
ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));
}
else
throw new UnsupportedOperationException("Unknown Collection type");
}
else if(form instanceof Keyword
|| form instanceof Number
|| form instanceof Character
|| form instanceof String)
ret = form;
else
ret = RT.list(Compiler.QUOTE, form);
if(form instanceof IObj && RT.meta(form) != null)
{
//filter line and column numbers
IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY).without(RT.COLUMN_KEY);
if(newMeta.count() > 0)
return RT.list(WITH_META, ret, syntaxQuote(((IObj) form).meta()));
}
return ret;
}
private static ISeq sqExpandList(ISeq seq) {
PersistentVector ret = PersistentVector.EMPTY;
for(; seq != null; seq = seq.next())
{
Object item = seq.first();
if(isUnquote(item))
ret = ret.cons(RT.list(LIST, RT.second(item)));
else if(isUnquoteSplicing(item))
ret = ret.cons(RT.second(item));
else
ret = ret.cons(RT.list(LIST, syntaxQuote(item)));
}
return ret.seq();
}
private static IPersistentVector flattenMap(Object form){
IPersistentVector keyvals = PersistentVector.EMPTY;
for(ISeq s = RT.seq(form); s != null; s = s.next())
{
IMapEntry e = (IMapEntry) s.first();
keyvals = (IPersistentVector) keyvals.cons(e.key());
keyvals = (IPersistentVector) keyvals.cons(e.val());
}
return keyvals;
}
}
static boolean isUnquoteSplicing(Object form){
return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE_SPLICING);
}
static boolean isUnquote(Object form){
return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE);
}
static class UnquoteReader extends AFn{
public Object invoke(Object reader, Object comma, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
int ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading character");
pendingForms = ensurePending(pendingForms);
if(ch == '@')
{
Object o = read(r, true, null, true, opts, pendingForms);
return RT.list(UNQUOTE_SPLICING, o);
}
else
{
unread(r, ch);
Object o = read(r, true, null, true, opts, pendingForms);
return RT.list(UNQUOTE, o);
}
}
}
public static class CharacterReader extends AFn{
public Object invoke(Object reader, Object backslash, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
int ch = read1(r);
if(ch == -1)
throw Util.runtimeException("EOF while reading character");
String token = readToken(r, (char) ch);
if(token.length() == 1)
return Character.valueOf(token.charAt(0));
else if(token.equals("newline"))
return '\n';
else if(token.equals("space"))
return ' ';
else if(token.equals("tab"))
return '\t';
else if(token.equals("backspace"))
return '\b';
else if(token.equals("formfeed"))
return '\f';
else if(token.equals("return"))
return '\r';
else if(token.startsWith("u"))
{
char c = (char) readUnicodeChar(token, 1, 4, 16);
if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
return c;
}
else if(token.startsWith("o"))
{
int len = token.length() - 1;
if(len > 3)
throw Util.runtimeException("Invalid octal escape sequence length: " + len);
int uc = readUnicodeChar(token, 1, len, 8);
if(uc > 0377)
throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
return (char) uc;
}
throw Util.runtimeException("Unsupported character: \\" + token);
}
}
public static class ListReader extends AFn{
public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
int line = -1;
int column = -1;
if(r instanceof LineNumberingPushbackReader)
{
line = ((LineNumberingPushbackReader) r).getLineNumber();
column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
}
List list = readDelimitedList(')', r, true, opts, ensurePending(pendingForms));
if(list.isEmpty())
return PersistentList.EMPTY;
IObj s = (IObj) PersistentList.create(list);
// IObj s = (IObj) RT.seq(list);
if(line != -1)
{
return s.withMeta(RT.map(RT.LINE_KEY, line, RT.COLUMN_KEY, column));
}
else
return s;
}
}
/*
static class CtorReader extends AFn{
static final Symbol cls = Symbol.intern("class");
public Object invoke(Object reader, Object leftangle) {
PushbackReader r = (PushbackReader) reader;
// #
// #
// #
List list = readDelimitedList('>', r, true);
if(list.isEmpty())
throw Util.runtimeException("Must supply 'class', classname or classname/staticMethod");
Symbol s = (Symbol) list.get(0);
Object[] args = list.subList(1, list.size()).toArray();
if(s.equals(cls))
{
return RT.classForName(args[0].toString());
}
else if(s.ns != null) //static method
{
String classname = s.ns;
String method = s.name;
return Reflector.invokeStaticMethod(classname, method, args);
}
else
{
return Reflector.invokeConstructor(RT.classForName(s.name), args);
}
}
}
*/
public static class EvalReader extends AFn{
public Object invoke(Object reader, Object eq, Object opts, Object pendingForms) {
if (!RT.booleanCast(RT.READEVAL.deref()))
{
throw Util.runtimeException("EvalReader not allowed when *read-eval* is false.");
}
PushbackReader r = (PushbackReader) reader;
Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
if(o instanceof Symbol)
{
return RT.classForName(o.toString());
}
else if(o instanceof IPersistentList)
{
Symbol fs = (Symbol) RT.first(o);
if(fs.equals(THE_VAR))
{
Symbol vs = (Symbol) RT.second(o);
return RT.var(vs.ns, vs.name); //Compiler.resolve((Symbol) RT.second(o),true);
}
if(fs.name.endsWith("."))
{
Object[] args = RT.toArray(RT.next(o));
return Reflector.invokeConstructor(RT.classForName(fs.name.substring(0, fs.name.length() - 1)), args);
}
if(Compiler.namesStaticMember(fs))
{
Object[] args = RT.toArray(RT.next(o));
return Reflector.invokeStaticMethod(fs.ns, fs.name, args);
}
Object v = Compiler.maybeResolveIn(Compiler.currentNS(), fs);
if(v instanceof Var)
{
return ((IFn) v).applyTo(RT.next(o));
}
throw Util.runtimeException("Can't resolve " + fs);
}
else
throw new IllegalArgumentException("Unsupported #= form");
}
}
//static class ArgVectorReader extends AFn{
// public Object invoke(Object reader, Object leftparen) {
// PushbackReader r = (PushbackReader) reader;
// return ArgVector.create(readDelimitedList('|', r, true));
// }
//
//}
public static class VectorReader extends AFn{
public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts, ensurePending(pendingForms)));
}
}
public static class MapReader extends AFn{
public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
Object[] a = readDelimitedList('}', r, true, opts, ensurePending(pendingForms)).toArray();
if((a.length & 1) == 1)
throw Util.runtimeException("Map literal must contain an even number of forms");
return RT.map(a);
}
}
public static class SetReader extends AFn{
public Object invoke(Object reader, Object leftbracket, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true, opts, ensurePending(pendingForms)));
}
}
public static class UnmatchedDelimiterReader extends AFn{
public Object invoke(Object reader, Object rightdelim, Object opts, Object pendingForms) {
throw Util.runtimeException("Unmatched delimiter: " + rightdelim);
}
}
public static class UnreadableReader extends AFn{
public Object invoke(Object reader, Object leftangle, Object opts, Object pendingForms) {
throw Util.runtimeException("Unreadable form");
}
}
// Sentinel values for reading lists
private static final Object READ_EOF = new Object();
private static final Object READ_FINISHED = new Object();
public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive, Object opts, Object pendingForms) {
final int firstline =
(r instanceof LineNumberingPushbackReader) ?
((LineNumberingPushbackReader) r).getLineNumber() : -1;
ArrayList a = new ArrayList();
for(; ;) {
Object form = read(r, false, READ_EOF, delim, READ_FINISHED, isRecursive, opts, pendingForms);
if (form == READ_EOF) {
if (firstline < 0)
throw Util.runtimeException("EOF while reading");
else
throw Util.runtimeException("EOF while reading, starting at line " + firstline);
} else if (form == READ_FINISHED) {
return a;
}
a.add(form);
}
}
public static class CtorReader extends AFn{
public Object invoke(Object reader, Object firstChar, Object opts, Object pendingForms){
PushbackReader r = (PushbackReader) reader;
pendingForms = ensurePending(pendingForms);
Object name = read(r, true, null, false, opts, pendingForms);
if (!(name instanceof Symbol))
throw new RuntimeException("Reader tag must be a symbol");
Symbol sym = (Symbol)name;
Object form = read(r, true, null, true, opts, pendingForms);
if(isPreserveReadCond(opts) || RT.suppressRead()) {
return TaggedLiteral.create(sym, form);
} else {
return sym.getName().contains(".") ? readRecord(form, sym, opts, pendingForms) : readTagged(form, sym, opts, pendingForms);
}
}
private Object readTagged(Object o, Symbol tag, Object opts, Object pendingForms){
ILookup data_readers = (ILookup)RT.DATA_READERS.deref();
IFn data_reader = (IFn)RT.get(data_readers, tag);
if(data_reader == null){
data_readers = (ILookup)RT.DEFAULT_DATA_READERS.deref();
data_reader = (IFn)RT.get(data_readers, tag);
if(data_reader == null){
IFn default_reader = (IFn)RT.DEFAULT_DATA_READER_FN.deref();
if(default_reader != null)
return default_reader.invoke(tag, o);
else
throw new RuntimeException("No reader function for tag " + tag.toString());
}
}
return data_reader.invoke(o);
}
private Object readRecord(Object form, Symbol recordName, Object opts, Object pendingForms){
boolean readeval = RT.booleanCast(RT.READEVAL.deref());
if(!readeval)
{
throw Util.runtimeException("Record construction syntax can only be used when *read-eval* == true");
}
Class recordClass = RT.classForNameNonLoading(recordName.toString());
boolean shortForm = true;
if(form instanceof IPersistentMap) {
shortForm = false;
} else if (form instanceof IPersistentVector) {
shortForm = true;
} else {
throw Util.runtimeException("Unreadable constructor form starting with \"#" + recordName + "\"");
}
Object ret = null;
Constructor[] allctors = ((Class)recordClass).getConstructors();
if(shortForm)
{
IPersistentVector recordEntries = (IPersistentVector)form;
boolean ctorFound = false;
for (Constructor ctor : allctors)
if(ctor.getParameterTypes().length == recordEntries.count())
ctorFound = true;
if(!ctorFound)
throw Util.runtimeException("Unexpected number of constructor arguments to " + recordClass.toString() + ": got " + recordEntries.count());
ret = Reflector.invokeConstructor(recordClass, RT.toArray(recordEntries));
}
else
{
IPersistentMap vals = (IPersistentMap)form;
for(ISeq s = RT.keys(vals); s != null; s = s.next())
{
if(!(s.first() instanceof Keyword))
throw Util.runtimeException("Unreadable defrecord form: key must be of type clojure.lang.Keyword, got " + s.first().toString());
}
ret = Reflector.invokeStaticMethod(recordClass, "create", new Object[]{vals});
}
return ret;
}
}
static boolean isPreserveReadCond(Object opts) {
if(RT.booleanCast(READ_COND_ENV.deref()) && opts instanceof IPersistentMap)
{
Object readCond = ((IPersistentMap) opts).valAt(OPT_READ_COND);
return COND_PRESERVE.equals(readCond);
}
else
return false;
}
public static class ConditionalReader extends AFn {
final static public Keyword DEFAULT_FEATURE = Keyword.intern(null, "default");
final static public IPersistentSet RESERVED_FEATURES =
RT.set(Keyword.intern(null, "else"), Keyword.intern(null, "none"));
public static boolean hasFeature(Object feature, Object opts) {
if (! (feature instanceof Keyword))
throw Util.runtimeException("Feature should be a keyword: " + feature);
if(DEFAULT_FEATURE.equals(feature))
return true;
IPersistentSet custom = (IPersistentSet) ((IPersistentMap)opts).valAt(OPT_FEATURES);
return custom != null && custom.contains(feature);
}
public static Object readCondDelimited(PushbackReader r, boolean splicing, Object opts, Object pendingForms) {
Object result = null;
Object form; // The most recently ready form
boolean toplevel = (pendingForms == null);
pendingForms = ensurePending(pendingForms);
final int firstline =
(r instanceof LineNumberingPushbackReader) ?
((LineNumberingPushbackReader) r).getLineNumber() : -1;
for(; ;) {
if(result == null) {
// Read the next feature
form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms);
if (form == READ_EOF) {
if (firstline < 0)
throw Util.runtimeException("EOF while reading");
else
throw Util.runtimeException("EOF while reading, starting at line " + firstline);
} else if (form == READ_FINISHED) {
break; // read-cond form is done
}
if(RESERVED_FEATURES.contains(form))
throw Util.runtimeException("Feature name " + form + " is reserved.");
if (hasFeature(form, opts)) {
//Read the form corresponding to the feature, and assign it to result if everything is kosher
form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms);
if (form == READ_EOF) {
if (firstline < 0)
throw Util.runtimeException("EOF while reading");
else
throw Util.runtimeException("EOF while reading, starting at line " + firstline);
} else if (form == READ_FINISHED) {
if (firstline < 0)
throw Util.runtimeException("read-cond requires an even number of forms.");
else
throw Util.runtimeException("read-cond starting on line " + firstline + " requires an even number of forms");
} else {
result = form;
}
}
}
// When we already have a result, or when the feature didn't match, discard the next form in the reader
try {
Var.pushThreadBindings(RT.map(RT.SUPPRESS_READ, RT.T));
form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms);
if (form == READ_EOF) {
if (firstline < 0)
throw Util.runtimeException("EOF while reading");
else
throw Util.runtimeException("EOF while reading, starting at line " + firstline);
} else if (form == READ_FINISHED) {
break;
}
}
finally {
Var.popThreadBindings();
}
}
if (result == null) // no features matched
return r;
if (splicing) {
if(! (result instanceof List))
throw Util.runtimeException("Spliced form list in read-cond-splicing must implement java.util.List");
if(toplevel)
throw Util.runtimeException("Reader conditional splicing not allowed at the top level.");
((List)pendingForms).addAll(0, (List)result);
return r;
} else {
return result;
}
};
private static void checkConditionalAllowed(Object opts) {
IPersistentMap mopts = (IPersistentMap)opts;
if(! (opts != null && (COND_ALLOW.equals(mopts.valAt(OPT_READ_COND)) ||
COND_PRESERVE.equals(mopts.valAt(OPT_READ_COND)))))
throw Util.runtimeException("Conditional read not allowed");
}
public Object invoke(Object reader, Object mode, Object opts, Object pendingForms) {
checkConditionalAllowed(opts);
PushbackReader r = (PushbackReader) reader;
int ch = read1(r);
if (ch == -1)
throw Util.runtimeException("EOF while reading character");
boolean splicing = false;
if (ch == '@') {
splicing = true;
ch = read1(r);
}
while(isWhitespace(ch))
ch = read1(r);
if (ch == -1)
throw Util.runtimeException("EOF while reading character");
if(ch != '(')
throw Util.runtimeException("read-cond body must be a list");
try {
Var.pushThreadBindings(RT.map(READ_COND_ENV, RT.T));
if (isPreserveReadCond(opts)) {
IFn listReader = getMacro(ch); // should always be a list
Object form = listReader.invoke(r, ch, opts, ensurePending(pendingForms));
return ReaderConditional.create(form, splicing);
} else {
return readCondDelimited(r, splicing, opts, pendingForms);
}
} finally {
Var.popThreadBindings();
}
}
}
/*
public static void main(String[] args) throws Exception{
//RT.init();
PushbackReader rdr = new PushbackReader( new java.io.StringReader( "(+ 21 21)" ) );
Object input = LispReader.read(rdr, false, new Object(), false );
System.out.println(Compiler.eval(input));
}
public static void main(String[] args){
LineNumberingPushbackReader r = new LineNumberingPushbackReader(new InputStreamReader(System.in));
OutputStreamWriter w = new OutputStreamWriter(System.out);
Object ret = null;
try
{
for(; ;)
{
ret = LispReader.read(r, true, null, false);
RT.print(ret, w);
w.write('\n');
if(ret != null)
w.write(ret.getClass().toString());
w.write('\n');
w.flush();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
*/
}
================================================
FILE: src/jvm/clojure/lang/LockingTransaction.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 26, 2007 */
package clojure.lang;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
//@SuppressWarnings({"SynchronizeOnNonFinalField"})
public class LockingTransaction{
public static final int RETRY_LIMIT = 10000;
public static final int LOCK_WAIT_MSECS = 100;
public static final long BARGE_WAIT_NANOS = 10 * 1000000;
//public static int COMMUTE_RETRY_LIMIT = 10;
static final int RUNNING = 0;
static final int COMMITTING = 1;
static final int RETRY = 2;
static final int KILLED = 3;
static final int COMMITTED = 4;
final static ThreadLocal transaction = new ThreadLocal();
static class RetryEx extends Error{
}
static class AbortException extends Exception{
}
public static class Info{
final AtomicInteger status;
final long startPoint;
final CountDownLatch latch;
public Info(int status, long startPoint){
this.status = new AtomicInteger(status);
this.startPoint = startPoint;
this.latch = new CountDownLatch(1);
}
public boolean running(){
int s = status.get();
return s == RUNNING || s == COMMITTING;
}
}
static class CFn{
final IFn fn;
final ISeq args;
public CFn(IFn fn, ISeq args){
this.fn = fn;
this.args = args;
}
}
//total order on transactions
//transactions will consume a point for init, for each retry, and on commit if writing
final private static AtomicLong lastPoint = new AtomicLong();
void getReadPoint(){
readPoint = lastPoint.incrementAndGet();
}
long getCommitPoint(){
return lastPoint.incrementAndGet();
}
void stop(int status){
if(info != null)
{
synchronized(info)
{
info.status.set(status);
info.latch.countDown();
}
info = null;
vals.clear();
sets.clear();
commutes.clear();
//actions.clear();
}
}
Info info;
long readPoint;
long startPoint;
long startTime;
static RetryEx retryex_;
static RetryEx retryex() {
if (retryex_ == null) {
retryex_ = new RetryEx();
}
return retryex_;
}
final ArrayList actions = new ArrayList();
final HashMap vals = new HashMap();
final HashSet sets = new HashSet();
final TreeMap> commutes = new TreeMap>();
final HashSet ensures = new HashSet(); //all hold readLock
void tryWriteLock(Ref ref){
try
{
if(!ref.lock.writeLock().tryLock(LOCK_WAIT_MSECS, TimeUnit.MILLISECONDS))
throw retryex();
}
catch(InterruptedException e)
{
throw retryex();
}
}
//returns the most recent val
Object lock(Ref ref){
//can't upgrade readLock, so release it
releaseIfEnsured(ref);
boolean unlocked = true;
try
{
tryWriteLock(ref);
unlocked = false;
if(ref.tvals != null && ref.tvals.point > readPoint)
throw retryex();
Info refinfo = ref.tinfo;
//write lock conflict
if(refinfo != null && refinfo != info && refinfo.running())
{
if(!barge(refinfo))
{
ref.lock.writeLock().unlock();
unlocked = true;
return blockAndBail(refinfo);
}
}
ref.tinfo = info;
return ref.tvals == null ? null : ref.tvals.val;
}
finally
{
if(!unlocked)
ref.lock.writeLock().unlock();
}
}
private Object blockAndBail(Info refinfo){
//stop prior to blocking
stop(RETRY);
try
{
refinfo.latch.await(LOCK_WAIT_MSECS, TimeUnit.MILLISECONDS);
}
catch(InterruptedException e)
{
//ignore
}
throw retryex();
}
private void releaseIfEnsured(Ref ref){
if(ensures.contains(ref))
{
ensures.remove(ref);
ref.lock.readLock().unlock();
}
}
void abort() throws AbortException{
stop(KILLED);
throw new AbortException();
}
private boolean bargeTimeElapsed(){
return System.nanoTime() - startTime > BARGE_WAIT_NANOS;
}
private boolean barge(Info refinfo){
boolean barged = false;
//if this transaction is older
// try to abort the other
if(bargeTimeElapsed() && startPoint < refinfo.startPoint)
{
barged = refinfo.status.compareAndSet(RUNNING, KILLED);
if(barged)
refinfo.latch.countDown();
}
return barged;
}
static LockingTransaction getEx(){
LockingTransaction t = transaction.get();
if(t == null || t.info == null)
throw new IllegalStateException("No transaction running");
return t;
}
static public boolean isRunning(){
return getRunning() != null;
}
static LockingTransaction getRunning(){
LockingTransaction t = transaction.get();
if(t == null || t.info == null)
return null;
return t;
}
static public Object runInTransaction(Callable fn) throws Exception{
LockingTransaction t = transaction.get();
Object ret;
if(t == null) {
transaction.set(t = new LockingTransaction());
try {
ret = t.run(fn);
} finally {
transaction.remove();
}
} else {
if(t.info != null) {
ret = fn.call();
} else {
ret = t.run(fn);
}
}
return ret;
}
static class Notify{
final public Ref ref;
final public Object oldval;
final public Object newval;
Notify(Ref ref, Object oldval, Object newval){
this.ref = ref;
this.oldval = oldval;
this.newval = newval;
}
}
Object run(Callable fn) throws Exception{
boolean done = false;
Object ret = null;
ArrayList locked = new ArrayList();
ArrayList notify = new ArrayList();
for(int i = 0; !done && i < RETRY_LIMIT; i++)
{
try
{
getReadPoint();
if(i == 0)
{
startPoint = readPoint;
startTime = System.nanoTime();
}
info = new Info(RUNNING, startPoint);
ret = fn.call();
//make sure no one has killed us before this point, and can't from now on
if(info.status.compareAndSet(RUNNING, COMMITTING))
{
for(Map.Entry> e : commutes.entrySet())
{
Ref ref = e.getKey();
if(sets.contains(ref)) continue;
boolean wasEnsured = ensures.contains(ref);
//can't upgrade readLock, so release it
releaseIfEnsured(ref);
tryWriteLock(ref);
locked.add(ref);
if(wasEnsured && ref.tvals != null && ref.tvals.point > readPoint)
throw retryex();
Info refinfo = ref.tinfo;
if(refinfo != null && refinfo != info && refinfo.running())
{
if(!barge(refinfo))
throw retryex();
}
Object val = ref.tvals == null ? null : ref.tvals.val;
vals.put(ref, val);
for(CFn f : e.getValue())
{
vals.put(ref, f.fn.applyTo(RT.cons(vals.get(ref), f.args)));
}
}
for(Ref ref : sets)
{
tryWriteLock(ref);
locked.add(ref);
}
//validate and enqueue notifications
for(Map.Entry e : vals.entrySet())
{
Ref ref = e.getKey();
ref.validate(ref.getValidator(), e.getValue());
}
//at this point, all values calced, all refs to be written locked
//no more client code to be called
long commitPoint = getCommitPoint();
for(Map.Entry e : vals.entrySet())
{
Ref ref = e.getKey();
Object oldval = ref.tvals == null ? null : ref.tvals.val;
Object newval = e.getValue();
int hcount = ref.histCount();
if(ref.tvals == null)
{
ref.tvals = new Ref.TVal(newval, commitPoint);
}
else if((ref.faults.get() > 0 && hcount < ref.maxHistory)
|| hcount < ref.minHistory)
{
ref.tvals = new Ref.TVal(newval, commitPoint, ref.tvals);
ref.faults.set(0);
}
else
{
ref.tvals = ref.tvals.next;
ref.tvals.val = newval;
ref.tvals.point = commitPoint;
}
if(ref.getWatches().count() > 0)
notify.add(new Notify(ref, oldval, newval));
}
done = true;
info.status.set(COMMITTED);
}
}
catch(RetryEx retry)
{
//eat this so we retry rather than fall out
}
finally
{
for(int k = locked.size() - 1; k >= 0; --k)
{
locked.get(k).lock.writeLock().unlock();
}
locked.clear();
for(Ref r : ensures)
{
r.lock.readLock().unlock();
}
ensures.clear();
stop(done ? COMMITTED : RETRY);
try
{
if(done) //re-dispatch out of transaction
{
for(Notify n : notify)
{
n.ref.notifyWatches(n.oldval, n.newval);
}
for(Agent.Action action : actions)
{
Agent.dispatchAction(action);
}
}
}
finally
{
notify.clear();
actions.clear();
}
}
}
if(!done)
throw Util.runtimeException("Transaction failed after reaching retry limit");
return ret;
}
public void enqueue(Agent.Action action){
actions.add(action);
}
Object doGet(Ref ref){
if(!info.running())
throw retryex();
if(vals.containsKey(ref))
return vals.get(ref);
try
{
ref.lock.readLock().lock();
if(ref.tvals == null)
throw new IllegalStateException(ref.toString() + " is unbound.");
Ref.TVal ver = ref.tvals;
do
{
if(ver.point <= readPoint)
return ver.val;
} while((ver = ver.prior) != ref.tvals);
}
finally
{
ref.lock.readLock().unlock();
}
//no version of val precedes the read point
ref.faults.incrementAndGet();
throw retryex();
}
Object doSet(Ref ref, Object val){
if(!info.running())
throw retryex();
if(commutes.containsKey(ref))
throw new IllegalStateException("Can't set after commute");
if(!sets.contains(ref))
{
sets.add(ref);
lock(ref);
}
vals.put(ref, val);
return val;
}
void doEnsure(Ref ref){
if(!info.running())
throw retryex();
if(ensures.contains(ref))
return;
ref.lock.readLock().lock();
//someone completed a write after our snapshot
if(ref.tvals != null && ref.tvals.point > readPoint) {
ref.lock.readLock().unlock();
throw retryex();
}
Info refinfo = ref.tinfo;
//writer exists
if(refinfo != null && refinfo.running())
{
ref.lock.readLock().unlock();
if(refinfo != info) //not us, ensure is doomed
{
blockAndBail(refinfo);
}
}
else
ensures.add(ref);
}
Object doCommute(Ref ref, IFn fn, ISeq args) {
if(!info.running())
throw retryex();
if(!vals.containsKey(ref))
{
Object val = null;
try
{
ref.lock.readLock().lock();
val = ref.tvals == null ? null : ref.tvals.val;
}
finally
{
ref.lock.readLock().unlock();
}
vals.put(ref, val);
}
ArrayList fns = commutes.get(ref);
if(fns == null)
commutes.put(ref, fns = new ArrayList());
fns.add(new CFn(fn, args));
Object ret = fn.applyTo(RT.cons(vals.get(ref), args));
vals.put(ref, ret);
return ret;
}
/*
//for test
static CyclicBarrier barrier;
static ArrayList items;
public static void main(String[] args){
try
{
if(args.length != 4)
System.err.println("Usage: LockingTransaction nthreads nitems niters ninstances");
int nthreads = Integer.parseInt(args[0]);
int nitems = Integer.parseInt(args[1]);
int niters = Integer.parseInt(args[2]);
int ninstances = Integer.parseInt(args[3]);
if(items == null)
{
ArrayList temp = new ArrayList(nitems);
for(int i = 0; i < nitems; i++)
temp.add(new Ref(0));
items = temp;
}
class Incr extends AFn{
public Object invoke(Object arg1) {
Integer i = (Integer) arg1;
return i + 1;
}
public Obj withMeta(IPersistentMap meta){
throw new UnsupportedOperationException();
}
}
class Commuter extends AFn implements Callable{
int niters;
List items;
Incr incr;
public Commuter(int niters, List items){
this.niters = niters;
this.items = items;
this.incr = new Incr();
}
public Object call() {
long nanos = 0;
for(int i = 0; i < niters; i++)
{
long start = System.nanoTime();
LockingTransaction.runInTransaction(this);
nanos += System.nanoTime() - start;
}
return nanos;
}
public Object invoke() {
for(Ref tref : items)
{
LockingTransaction.getEx().doCommute(tref, incr);
}
return null;
}
public Obj withMeta(IPersistentMap meta){
throw new UnsupportedOperationException();
}
}
class Incrementer extends AFn implements Callable{
int niters;
List items;
public Incrementer(int niters, List items){
this.niters = niters;
this.items = items;
}
public Object call() {
long nanos = 0;
for(int i = 0; i < niters; i++)
{
long start = System.nanoTime();
LockingTransaction.runInTransaction(this);
nanos += System.nanoTime() - start;
}
return nanos;
}
public Object invoke() {
for(Ref tref : items)
{
//Transaction.get().doTouch(tref);
// LockingTransaction t = LockingTransaction.getEx();
// int val = (Integer) t.doGet(tref);
// t.doSet(tref, val + 1);
int val = (Integer) tref.get();
tref.set(val + 1);
}
return null;
}
public Obj withMeta(IPersistentMap meta){
throw new UnsupportedOperationException();
}
}
ArrayList> tasks = new ArrayList(nthreads);
for(int i = 0; i < nthreads; i++)
{
ArrayList si;
synchronized(items)
{
si = (ArrayList) items.clone();
}
Collections.shuffle(si);
tasks.add(new Incrementer(niters, si));
//tasks.add(new Commuter(niters, si));
}
ExecutorService e = Executors.newFixedThreadPool(nthreads);
if(barrier == null)
barrier = new CyclicBarrier(ninstances);
System.out.println("waiting for other instances...");
barrier.await();
System.out.println("starting");
long start = System.nanoTime();
List> results = e.invokeAll(tasks);
long estimatedTime = System.nanoTime() - start;
System.out.printf("nthreads: %d, nitems: %d, niters: %d, time: %d%n", nthreads, nitems, niters,
estimatedTime / 1000000);
e.shutdown();
for(Future result : results)
{
System.out.printf("%d, ", result.get() / 1000000);
}
System.out.println();
System.out.println("waiting for other instances...");
barrier.await();
synchronized(items)
{
for(Ref item : items)
{
System.out.printf("%d, ", (Integer) item.currentVal());
}
}
System.out.println("\ndone");
System.out.flush();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
*/
}
================================================
FILE: src/jvm/clojure/lang/LongRange.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Implements the special common case of a finite range based on long start, end, and step.
*/
public class LongRange extends ASeq implements Counted, IChunkedSeq, IReduce {
private static final int CHUNK_SIZE = 32;
// Invariants guarantee this is never an empty or infinite seq
// assert(start != end && step != 0)
final long start;
final long end;
final long step;
final BoundsCheck boundsCheck;
private volatile LongChunk _chunk; // lazy
private volatile ISeq _chunkNext; // lazy
private volatile ISeq _next; // cached
private static interface BoundsCheck extends Serializable {
boolean exceededBounds(long val);
}
private static BoundsCheck positiveStep(final long end) {
return new BoundsCheck() {
public boolean exceededBounds(long val){
return (val >= end);
}
};
}
private static BoundsCheck negativeStep(final long end) {
return new BoundsCheck() {
public boolean exceededBounds(long val){
return (val <= end);
}
};
}
private LongRange(long start, long end, long step, BoundsCheck boundsCheck){
this.start = start;
this.end = end;
this.step = step;
this.boundsCheck = boundsCheck;
}
private LongRange(long start, long end, long step, BoundsCheck boundsCheck, LongChunk chunk, ISeq chunkNext){
this.start = start;
this.end = end;
this.step = step;
this.boundsCheck = boundsCheck;
this._chunk = chunk;
this._chunkNext = chunkNext;
}
private LongRange(IPersistentMap meta, long start, long end, long step, BoundsCheck boundsCheck, LongChunk chunk, ISeq chunkNext){
super(meta);
this.start = start;
this.end = end;
this.step = step;
this.boundsCheck = boundsCheck;
this._chunk = chunk;
this._chunkNext = chunkNext;
}
public static ISeq create(long end) {
if(end > 0)
return new LongRange(0L, end, 1L, positiveStep(end));
return PersistentList.EMPTY;
}
public static ISeq create(long start, long end) {
if(start >= end)
return PersistentList.EMPTY;
return new LongRange(start, end, 1L, positiveStep(end));
}
public static ISeq create(final long start, long end, long step) {
if(step > 0) {
if(end <= start) return PersistentList.EMPTY;
return new LongRange(start, end, step, positiveStep(end));
} else if(step < 0) {
if(end >= start) return PersistentList.EMPTY;
return new LongRange(start, end, step, negativeStep(end));
} else {
if(end == start) return PersistentList.EMPTY;
return Repeat.create(start);
}
}
public Obj withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
return new LongRange(meta, start, end, step, boundsCheck, _chunk, _chunkNext);
}
public Object first() {
return start;
}
public void forceChunk() {
if(_chunk != null) return;
long count;
try {
count = rangeCount(start, end, step);
} catch(ArithmeticException e) {
// size of total range is > Long.MAX_VALUE so must step to count
// this only happens in pathological range cases like:
// (range -9223372036854775808 9223372036854775807 9223372036854775807)
count = steppingCount(start, end, step);
}
if (count > CHUNK_SIZE) { // not last chunk
long nextStart = start + (step * CHUNK_SIZE); // cannot overflow, must be < end
_chunk = new LongChunk(start, step, CHUNK_SIZE);
_chunkNext = new LongRange(nextStart, end, step, boundsCheck);
} else { // last chunk
_chunk = new LongChunk(start, step, (int) count); // count must be <= CHUNK_SIZE
}
}
public ISeq next() {
if(_next != null)
return _next;
forceChunk();
if(_chunk.count() > 1) {
LongChunk smallerChunk = _chunk.dropFirst();
_next = new LongRange(smallerChunk.first(), end, step, boundsCheck, smallerChunk, _chunkNext);
return _next;
}
return chunkedNext();
}
public IChunk chunkedFirst() {
forceChunk();
return _chunk;
}
public ISeq chunkedNext() {
return chunkedMore().seq();
}
public ISeq chunkedMore() {
forceChunk();
if(_chunkNext == null)
return PersistentList.EMPTY;
return _chunkNext;
}
// fallback count mechanism for pathological cases
// returns either exact count or CHUNK_SIZE+1
long steppingCount(long start, long end, long step) {
long count = 1;
long s = start;
while(count <= CHUNK_SIZE) {
try {
s = Numbers.add(s, step);
if(boundsCheck.exceededBounds(s))
break;
else
count++;
} catch(ArithmeticException e) {
break;
}
}
return count;
}
// returns exact size of remaining items OR throws ArithmeticException for overflow case
long rangeCount(long start, long end, long step) {
// (1) count = ceiling ( (end - start) / step )
// (2) ceiling(a/b) = (a+b+o)/b where o=-1 for positive stepping and +1 for negative stepping
// thus: count = end - start + step + o / step
return Numbers.add(Numbers.add(Numbers.minus(end, start), step), this.step > 0 ? -1 : 1) / step;
}
public int count() {
try {
long c = rangeCount(start, end, step);
if(c > Integer.MAX_VALUE) {
return Numbers.throwIntOverflow();
} else {
return (int) c;
}
} catch(ArithmeticException e) {
// rare case from large range or step, fall back to iterating and counting
Iterator iter = this.iterator();
long count = 0;
while(iter.hasNext()) {
iter.next();
count++;
}
if(count > Integer.MAX_VALUE)
return Numbers.throwIntOverflow();
else
return (int)count;
}
}
public Object reduce(IFn f) {
Object acc = start;
long i = start + step;
while(! boundsCheck.exceededBounds(i)) {
acc = f.invoke(acc, i);
if (acc instanceof Reduced) return ((Reduced)acc).deref();
i += step;
}
return acc;
}
public Object reduce(IFn f, Object val) {
Object acc = val;
long i = start;
do {
acc = f.invoke(acc, i);
if (RT.isReduced(acc)) return ((Reduced)acc).deref();
i += step;
} while(! boundsCheck.exceededBounds(i));
return acc;
}
public Iterator iterator() {
return new LongRangeIterator();
}
class LongRangeIterator implements Iterator {
private long next;
private boolean hasNext;
public LongRangeIterator() {
this.next = start;
this.hasNext = true;
}
public boolean hasNext() {
return hasNext;
}
public Object next() {
if (hasNext) {
long ret = next;
try {
next = Numbers.add(next, step);
hasNext = ! boundsCheck.exceededBounds(next);
} catch(ArithmeticException e) {
hasNext = false;
}
return ret;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static class LongChunk implements IChunk, Serializable {
final long start;
final long step;
final int count;
public LongChunk(long start, long step, int count) {
this.start = start;
this.step = step;
this.count = count;
}
public long first(){
return start;
}
public Object nth(int i){
return start + (i * step);
}
public Object nth(int i, Object notFound){
if(i >= 0 && i < count)
return start + (i * step);
return notFound;
}
public int count(){
return count;
}
public LongChunk dropFirst(){
if(count <= 1)
throw new IllegalStateException("dropFirst of empty chunk");
return new LongChunk(start+step, step, count-1);
}
public Object reduce(IFn f, Object init) {
long x = start;
Object ret = init;
for(int i=0; i> shift) & mask) << 1;
if(idx < table.length && table[idx] == c)
{
Entry e = ((Entry) table[idx + 1]);
mre = e;
return e != null ? e.fn : null;
}
return null;
}
}
}
================================================
FILE: src/jvm/clojure/lang/MultiFn.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Sep 13, 2007 */
package clojure.lang;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*-[
#include "clojure/core_assoc.h"
#include "clojure/core_dissoc.h"
#include "clojure/core_isa_QMARK_.h"
#include "clojure/core_parents.h"
]-*/
public class MultiFn extends AFn {
final public IFn dispatchFn;
final public Object defaultDispatchVal;
final public IRef hierarchy;
final String name;
final ReentrantReadWriteLock rw;
volatile IPersistentMap methodTable;
volatile IPersistentMap preferTable;
volatile IPersistentMap methodCache;
volatile Object cachedHierarchy;
static final Var assoc = RT.var("clojure.core", "assoc");
static final Var dissoc = RT.var("clojure.core", "dissoc");
static final Var isa = RT.var("clojure.core", "isa?");
static final Var parents = RT.var("clojure.core", "parents");
static native void loadFns() /*-[
Clojurecore_assoc_get_VAR_();
Clojurecore_dissoc_get_VAR_();
Clojurecore_isa_QMARK__get_VAR_();
Clojurecore_parents_get_VAR_();
]-*/;
static {
if (ObjC.objc) {
loadFns();
} else {
assoc.maybeLoad();
dissoc.maybeLoad();
isa.maybeLoad();
parents.maybeLoad();
}
}
public MultiFn(String name, IFn dispatchFn, Object defaultDispatchVal,
IRef hierarchy) {
this.rw = new ReentrantReadWriteLock();
this.name = name;
this.dispatchFn = dispatchFn;
this.defaultDispatchVal = defaultDispatchVal;
this.methodTable = PersistentHashMap.EMPTY;
this.methodCache = getMethodTable();
this.preferTable = PersistentHashMap.EMPTY;
this.hierarchy = hierarchy;
cachedHierarchy = null;
}
public MultiFn reset() {
rw.writeLock().lock();
try {
methodTable = methodCache = preferTable = PersistentHashMap.EMPTY;
cachedHierarchy = null;
return this;
} finally {
rw.writeLock().unlock();
}
}
public MultiFn addMethod(Object dispatchVal, IFn method) {
rw.writeLock().lock();
try {
methodTable = getMethodTable().assoc(dispatchVal, method);
resetCache();
return this;
} finally {
rw.writeLock().unlock();
}
}
public MultiFn removeMethod(Object dispatchVal) {
rw.writeLock().lock();
try {
methodTable = getMethodTable().without(dispatchVal);
resetCache();
return this;
} finally {
rw.writeLock().unlock();
}
}
public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
rw.writeLock().lock();
try {
// TODO: allow this in ios?
// This is very slow in objc runtime
if (!ObjC.objc && prefers(dispatchValY, dispatchValX))
throw new IllegalStateException(
String
.format(
"Preference conflict in multimethod '%s': %s is already preferred to %s",
name, dispatchValY, dispatchValX));
preferTable = getPreferTable().assoc(
dispatchValX,
RT.conj((IPersistentCollection) RT.get(getPreferTable(),
dispatchValX, PersistentHashSet.EMPTY), dispatchValY));
resetCache();
return this;
} finally {
rw.writeLock().unlock();
}
}
private boolean prefers(Object x, Object y) {
if (x == y)
return false;
if (x instanceof Class && y instanceof Class) {
for (ISeq ps = RT.keys(getPreferTable()); ps != null; ps = ps.next()) {
Class xx = (Class)x;
Class yy = (Class)y;
Object k = ps.first();
if (k instanceof Class && (x == k || ((Class) k).isAssignableFrom(xx))) {
IPersistentSet xprefs = (IPersistentSet) getPreferTable().valAt(k);
if (xprefs.contains(y)) {
return true;
}
for (ISeq xp = RT.seq(xprefs); xp != null; xp = xp.next()) {
Object p = xp.first();
if (p instanceof Class && (y == p || ((Class) p).isAssignableFrom(yy))) {
return true;
}
}
}
}
return false;
}
IPersistentSet xprefs = (IPersistentSet) getPreferTable().valAt(x);
if (xprefs != null && xprefs.contains(y))
return true;
for (ISeq ps = RT.seq(parents.invoke(y)); ps != null; ps = ps.next()) {
if (prefers(x, ps.first()))
return true;
}
for (ISeq ps = RT.seq(parents.invoke(x)); ps != null; ps = ps.next()) {
if (prefers(ps.first(), y))
return true;
}
return false;
}
private boolean isA(Object x, Object y) {
// in objc not all classes extend java.lang.Object
if (x instanceof Class && y == Object.class) {
return true;
}
if (x instanceof Class && y instanceof Class) {
return ((Class) y).isAssignableFrom((Class) x);
}
return RT.booleanCast(isa.invoke(hierarchy.deref(), x, y));
}
private boolean dominates(Object x, Object y) {
return prefers(x, y) || isA(x, y);
}
private IPersistentMap resetCache() {
rw.writeLock().lock();
try {
methodCache = getMethodTable();
cachedHierarchy = hierarchy.deref();
return methodCache;
} finally {
rw.writeLock().unlock();
}
}
public IFn getMethod(Object dispatchVal) {
if (cachedHierarchy != hierarchy.deref())
resetCache();
IFn targetFn = (IFn) methodCache.valAt(dispatchVal);
if (targetFn != null)
return targetFn;
return findAndCacheBestMethod(dispatchVal);
}
private IFn getFn(Object dispatchVal) {
IFn targetFn = getMethod(dispatchVal);
if (targetFn == null)
throw new IllegalArgumentException(String.format(
"No method in multimethod '%s' for dispatch value: %s", name,
dispatchVal));
return targetFn;
}
private IFn findAndCacheBestMethod(Object dispatchVal) {
rw.readLock().lock();
Object bestValue;
IPersistentMap mt = methodTable;
IPersistentMap pt = preferTable;
Object ch = cachedHierarchy;
try {
Map.Entry bestEntry = null;
for (Object o : getMethodTable()) {
Map.Entry e = (Map.Entry) o;
if (isA(dispatchVal, e.getKey())) {
if (bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
bestEntry = e;
if (!dominates(bestEntry.getKey(), e.getKey()))
throw new IllegalArgumentException(
String
.format(
"Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
name, dispatchVal, e.getKey(), bestEntry.getKey()));
}
}
if (bestEntry == null)
{
bestValue = methodTable.valAt(defaultDispatchVal);
if(bestValue == null)
return null;
} else {
bestValue = bestEntry.getValue();
}
} finally {
rw.readLock().unlock();
}
// ensure basis has stayed stable throughout, else redo
rw.writeLock().lock();
try {
if (mt == methodTable && pt == preferTable && ch == cachedHierarchy
&& cachedHierarchy == hierarchy.deref()) {
// place in cache
methodCache = methodCache.assoc(dispatchVal, bestValue);
return (IFn) bestValue;
} else {
resetCache();
return findAndCacheBestMethod(dispatchVal);
}
} finally {
rw.writeLock().unlock();
}
}
public Object invoke() {
return getFn(dispatchFn.invoke()).invoke();
}
public Object invoke(Object arg1) {
return getFn(dispatchFn.invoke(arg1)).invoke(Util.ret1(arg1, arg1 = null));
}
public Object invoke(Object arg1, Object arg2) {
return getFn(dispatchFn.invoke(arg1, arg2)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
return getFn(dispatchFn.invoke(arg1, arg2, arg3)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5) {
return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6) {
return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7) {
return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7))
.invoke(Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8))
.invoke(Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9))
.invoke(Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null),
Util.ret1(arg9, arg9 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null), Util.ret1(arg11, arg11 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null), Util.ret1(arg11, arg11 = null),
Util.ret1(arg12, arg12 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null), Util.ret1(arg11, arg11 = null),
Util.ret1(arg12, arg12 = null), Util.ret1(arg13, arg13 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null),
Util.ret1(arg9, arg9 = null), Util.ret1(arg10, arg10 = null),
Util.ret1(arg11, arg11 = null), Util.ret1(arg12, arg12 = null),
Util.ret1(arg13, arg13 = null), Util.ret1(arg14, arg14 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null),
Util.ret1(arg9, arg9 = null), Util.ret1(arg10, arg10 = null),
Util.ret1(arg11, arg11 = null), Util.ret1(arg12, arg12 = null),
Util.ret1(arg13, arg13 = null), Util.ret1(arg14, arg14 = null),
Util.ret1(arg15, arg15 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15, arg16)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null),
Util.ret1(arg9, arg9 = null), Util.ret1(arg10, arg10 = null),
Util.ret1(arg11, arg11 = null), Util.ret1(arg12, arg12 = null),
Util.ret1(arg13, arg13 = null), Util.ret1(arg14, arg14 = null),
Util.ret1(arg15, arg15 = null), Util.ret1(arg16, arg16 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17)).invoke(
Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null),
Util.ret1(arg9, arg9 = null), Util.ret1(arg10, arg10 = null),
Util.ret1(arg11, arg11 = null), Util.ret1(arg12, arg12 = null),
Util.ret1(arg13, arg13 = null), Util.ret1(arg14, arg14 = null),
Util.ret1(arg15, arg15 = null), Util.ret1(arg16, arg16 = null),
Util.ret1(arg17, arg17 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18))
.invoke(Util.ret1(arg1, arg1 = null), Util.ret1(arg2, arg2 = null),
Util.ret1(arg3, arg3 = null), Util.ret1(arg4, arg4 = null),
Util.ret1(arg5, arg5 = null), Util.ret1(arg6, arg6 = null),
Util.ret1(arg7, arg7 = null), Util.ret1(arg8, arg8 = null),
Util.ret1(arg9, arg9 = null), Util.ret1(arg10, arg10 = null),
Util.ret1(arg11, arg11 = null), Util.ret1(arg12, arg12 = null),
Util.ret1(arg13, arg13 = null), Util.ret1(arg14, arg14 = null),
Util.ret1(arg15, arg15 = null), Util.ret1(arg16, arg16 = null),
Util.ret1(arg17, arg17 = null), Util.ret1(arg18, arg18 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18,
arg19)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null), Util.ret1(arg11, arg11 = null),
Util.ret1(arg12, arg12 = null), Util.ret1(arg13, arg13 = null),
Util.ret1(arg14, arg14 = null), Util.ret1(arg15, arg15 = null),
Util.ret1(arg16, arg16 = null), Util.ret1(arg17, arg17 = null),
Util.ret1(arg18, arg18 = null), Util.ret1(arg19, arg19 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19,
Object arg20) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18,
arg19, arg20)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null), Util.ret1(arg11, arg11 = null),
Util.ret1(arg12, arg12 = null), Util.ret1(arg13, arg13 = null),
Util.ret1(arg14, arg14 = null), Util.ret1(arg15, arg15 = null),
Util.ret1(arg16, arg16 = null), Util.ret1(arg17, arg17 = null),
Util.ret1(arg18, arg18 = null), Util.ret1(arg19, arg19 = null),
Util.ret1(arg20, arg20 = null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5, Object arg6, Object arg7, Object arg8, Object arg9,
Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19,
Object arg20, Object... args) {
return getFn(
dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18,
arg19, arg20, args)).invoke(Util.ret1(arg1, arg1 = null),
Util.ret1(arg2, arg2 = null), Util.ret1(arg3, arg3 = null),
Util.ret1(arg4, arg4 = null), Util.ret1(arg5, arg5 = null),
Util.ret1(arg6, arg6 = null), Util.ret1(arg7, arg7 = null),
Util.ret1(arg8, arg8 = null), Util.ret1(arg9, arg9 = null),
Util.ret1(arg10, arg10 = null), Util.ret1(arg11, arg11 = null),
Util.ret1(arg12, arg12 = null), Util.ret1(arg13, arg13 = null),
Util.ret1(arg14, arg14 = null), Util.ret1(arg15, arg15 = null),
Util.ret1(arg16, arg16 = null), Util.ret1(arg17, arg17 = null),
Util.ret1(arg18, arg18 = null), Util.ret1(arg19, arg19 = null),
Util.ret1(arg20, arg20 = null), args);
}
public IPersistentMap getMethodTable() {
return methodTable;
}
public IPersistentMap getPreferTable() {
return preferTable;
}
}
================================================
FILE: src/jvm/clojure/lang/Murmur3.java
================================================
/*
* Copyright (C) 2011 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
/*
* MurmurHash3 was written by Austin Appleby, and is placed in the public
* domain. The author hereby disclaims copyright to this source code.
*/
/*
* Source:
* http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
* (Modified to adapt to Guava coding conventions and to use the HashFunction interface)
*/
/**
* Modified to remove stuff Clojure doesn't need, placed under clojure.lang namespace,
* all fns made static, added hashOrdered/Unordered
*/
package clojure.lang;
import java.io.Serializable;
import java.nio.ByteBuffer;
/**
* See http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp
* MurmurHash3_x86_32
*
* @author Austin Appleby
* @author Dimitris Andreou
* @author Kurt Alfred Kluever
*/
public final class Murmur3{
private static final int seed = 0;
private static final int C1 = 0xcc9e2d51;
private static final int C2 = 0x1b873593;
public static int hashInt(int input){
if(input == 0) return 0;
int k1 = mixK1(input);
int h1 = mixH1(seed, k1);
return fmix(h1, 4);
}
public static int hashLong(long input){
if(input == 0) return 0;
int low = (int) input;
int high = (int) (input >>> 32);
int k1 = mixK1(low);
int h1 = mixH1(seed, k1);
k1 = mixK1(high);
h1 = mixH1(h1, k1);
return fmix(h1, 8);
}
public static int hashUnencodedChars(CharSequence input){
int h1 = seed;
// step through the CharSequence 2 chars at a time
for(int i = 1; i < input.length(); i += 2)
{
int k1 = input.charAt(i - 1) | (input.charAt(i) << 16);
k1 = mixK1(k1);
h1 = mixH1(h1, k1);
}
// deal with any remaining characters
if((input.length() & 1) == 1)
{
int k1 = input.charAt(input.length() - 1);
k1 = mixK1(k1);
h1 ^= k1;
}
return fmix(h1, 2 * input.length());
}
public static int mixCollHash(int hash, int count){
int h1 = seed;
int k1 = mixK1(hash);
h1 = mixH1(h1, k1);
return fmix(h1, count);
}
public static int hashOrdered(Iterable xs){
int n = 0;
int hash = 1;
for(Object x : xs)
{
hash = 31 * hash + Util.hasheq(x);
++n;
}
return mixCollHash(hash, n);
}
public static int hashUnordered(Iterable xs){
int hash = 0;
int n = 0;
for(Object x : xs)
{
hash += Util.hasheq(x);
++n;
}
return mixCollHash(hash, n);
}
private static int mixK1(int k1){
k1 *= C1;
k1 = Integer.rotateLeft(k1, 15);
k1 *= C2;
return k1;
}
private static int mixH1(int h1, int k1){
h1 ^= k1;
h1 = Integer.rotateLeft(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
return h1;
}
// Finalization mix - force all bits of a hash block to avalanche
private static int fmix(int h1, int length){
h1 ^= length;
h1 ^= h1 >>> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >>> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >>> 16;
return h1;
}
}
================================================
FILE: src/jvm/clojure/lang/Named.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Sep 20, 2007 */
package clojure.lang;
public interface Named{
String getNamespace();
String getName();
}
================================================
FILE: src/jvm/clojure/lang/Namespace.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 23, 2008 */
package clojure.lang;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
public class Namespace extends AReference implements Serializable {
final public Symbol name;
transient final AtomicReference mappings = new AtomicReference();
transient final AtomicReference aliases = new AtomicReference();
transient final AtomicReference refers = new AtomicReference();
final static ConcurrentHashMap namespaces = new ConcurrentHashMap();
public String toString(){
return name.toString();
}
Namespace(Symbol name){
super(name.meta());
this.name = name;
mappings.set(RT.DEFAULT_IMPORTS);
aliases.set(RT.map());
refers.set(RT.map());
if (!name.name.equals("clojure.core")) {
referNs(RT.CLOJURE_NS, RT.map());
}
}
public static ISeq all(){
return RT.seq(namespaces.values());
}
public Symbol getName(){
return name;
}
public IPersistentMap getMappings(){
return mappings.get();
}
public Var intern(Symbol sym){
if(sym.ns != null)
{
throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
}
IPersistentMap map = getMappings();
Object o;
Var v = null;
while((o = map.valAt(sym)) == null)
{
if(v == null)
v = new Var(this, sym);
IPersistentMap newMap = map.assoc(sym, v);
mappings.compareAndSet(map, newMap);
map = getMappings();
}
if(o instanceof Var && ((Var) o).ns == this)
return (Var) o;
if(v == null)
v = new Var(this, sym);
//warnOrFailOnReplace(sym, o, v);
while(!mappings.compareAndSet(map, map.assoc(sym, v)))
map = getMappings();
return v;
}
private void warnOrFailOnReplace(Symbol sym, Object o, Object v){
if (o instanceof Var)
{
Namespace ns = ((Var)o).ns;
if (ns == this || (v instanceof Var && ((Var)v).ns == RT.CLOJURE_NS))
return;
if (ns != RT.CLOJURE_NS)
throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name);
}
RT.errPrintWriter().println("WARNING: " + sym + " already refers to: " + o + " in namespace: " + name
+ ", being replaced by: " + v);
}
Object reference(Symbol sym, Object val){
if(sym.ns != null)
{
throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
}
IPersistentMap map = getMappings();
Object o;
while((o = map.valAt(sym)) == null)
{
IPersistentMap newMap = map.assoc(sym, val);
mappings.compareAndSet(map, newMap);
map = getMappings();
}
if(o == val)
return o;
warnOrFailOnReplace(sym, o, val);
while(!mappings.compareAndSet(map, map.assoc(sym, val)))
map = getMappings();
return val;
}
public static boolean areDifferentInstancesOfSameClassName(Class cls1, Class cls2) {
return (cls1 != cls2) && (cls1.getName().equals(cls2.getName()));
}
Class referenceClass(Symbol sym, Class val){
if(sym.ns != null)
{
throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
}
IPersistentMap map = getMappings();
Class c = (Class) map.valAt(sym);
while((c == null) || (areDifferentInstancesOfSameClassName(c, val)))
{
IPersistentMap newMap = map.assoc(sym, val);
mappings.compareAndSet(map, newMap);
map = getMappings();
c = (Class) map.valAt(sym);
}
if(c == val)
return c;
throw new IllegalStateException(sym + " already refers to: " + c + " in namespace: " + name);
}
public void unmap(Symbol sym) {
if(sym.ns != null)
{
throw new IllegalArgumentException("Can't unintern namespace-qualified symbol");
}
IPersistentMap map = getMappings();
while(map.containsKey(sym))
{
IPersistentMap newMap = map.without(sym);
mappings.compareAndSet(map, newMap);
map = getMappings();
}
}
public Class importClass(Symbol sym, Class c){
return referenceClass(sym, c);
}
public Class importClass(Class c){
String n = c.getName();
return importClass(Symbol.intern(n.substring(n.lastIndexOf('.') + 1)), c);
}
public Var refer(Symbol sym, Var var){
return (Var) reference(sym, var);
}
static Keyword only = Keyword.intern("only");
static Keyword onlyAndRefer = Keyword.intern("onlyAndRefer");
static Keyword refer = Keyword.intern("refer");
static Keyword exclude = Keyword.intern("exclude");
static Keyword allkeyword = Keyword.intern("all");
static Keyword rename = Keyword.intern("rename");
public Namespace referNs(Object ns, IPersistentMap filters) {
Object refer = filters.valAt(Namespace.refer);
Object exclude = filters.valAt(Namespace.exclude);
Object only = filters.valAt(Namespace.only);
only = only == null ? RT.set() : PersistentHashSet.create(RT.seq(only));
Object rename = filters.valAt(Namespace.rename);
IPersistentSet onlyAndRefer = (IPersistentSet) only;
if (refer != null && refer instanceof Sequential) {
for (ISeq e = RT.seq(refer); e != null; e = e.next()) {
onlyAndRefer = (IPersistentSet) onlyAndRefer.cons(e.first());
}
}
filters = filters
.assoc(Namespace.onlyAndRefer, onlyAndRefer)
.assoc(
Namespace.refer,
refer == null && refer instanceof Sequential ? PersistentHashSet
.create(RT.seq(refer)) : refer)
.assoc(
Namespace.exclude,
exclude == null ? RT.set() : PersistentHashSet.create(RT
.seq(exclude))).assoc(Namespace.only, only)
.assoc(Namespace.rename, rename == null ? RT.map() : invert(rename));
boolean successful = false;
while (!successful) {
IPersistentMap expects = refers.get();
successful = refers.compareAndSet(expects, expects.assoc(ns, filters));
}
return this;
}
private IPersistentMap invert(Object m) {
IPersistentMap r = RT.map();
Object s = RT.seq(RT.keys(m));
while (s != null) {
Object k = RT.first(s);
r = r.assoc(RT.get(m, k), k);
s = RT.next(s);
}
return r;
}
public static Namespace findOrCreate(Symbol name){
Namespace ns = namespaces.get(name);
if(ns != null)
return ns;
Namespace newns = new Namespace(name);
ns = namespaces.putIfAbsent(name, newns);
return ns == null ? newns : ns;
}
public static Namespace remove(Symbol name){
if(name.equals(RT.CLOJURE_NS.name))
throw new IllegalArgumentException("Cannot remove clojure namespace");
return namespaces.remove(name);
}
public static Namespace find(Symbol name){
return namespaces.get(name);
}
public Object getMapping(Symbol name) {
Object val = mappings.get().valAt(name);
if (val == null && !ObjC.objc) {
val = Var.maybeLoadFromClass(this.name.toString(), name.toString());
if (val == null && this != RT.CLOJURE_NS) {
val = searchMapping(name);
if (val != null && val instanceof Var) {
refer(name, (Var) val);
}
}
return val;
}
return val;
}
private Object searchMapping(Symbol name) {
IPersistentMap m = refers.get();
for (ISeq s = m.seq(); s != null; s = s.next()) {
Object i = s.first();
Object o = null;
Namespace ns = (Namespace) RT.first(i);
IPersistentMap filters = (IPersistentMap) RT.second(i);
Object refer = filters.valAt(Namespace.refer);
IPersistentSet exclude = (IPersistentSet) filters
.valAt(Namespace.exclude);
IPersistentMap rename = (IPersistentMap) filters.valAt(Namespace.rename);
if (exclude.contains(name)) {
continue;
} else if (rename.containsKey(name)) {
o = ns.getMapping((Symbol) rename.valAt(name));
} else if (Namespace.allkeyword.equals(refer)) {
o = ns.getMapping(name);
} else {
IPersistentSet onlyAndRefer = (IPersistentSet) filters
.valAt(Namespace.onlyAndRefer);
if (onlyAndRefer.count() > 0 && !onlyAndRefer.contains(name)) {
continue;
}
o = ns.getMapping(name);
}
if (o != null && o instanceof Var) {
return o;
}
}
return null;
}
public Var findInternedVar(Symbol symbol){
Object o = getMapping(symbol);
if(o != null && o instanceof Var && ((Var) o).ns == this)
return (Var) o;
if (!ObjC.objc) {
return Var.maybeLoadFromClass(name.toString(), symbol.toString());
} else {
return null;
}
}
public IPersistentMap getAliases(){
return aliases.get();
}
public Namespace lookupAlias(Symbol alias){
IPersistentMap map = getAliases();
return (Namespace) map.valAt(alias);
}
public void addAlias(Symbol alias, Namespace ns){
if (alias == null || ns == null)
throw new NullPointerException("Expecting Symbol + Namespace");
IPersistentMap map = getAliases();
while(!map.containsKey(alias))
{
IPersistentMap newMap = map.assoc(alias, ns);
aliases.compareAndSet(map, newMap);
map = getAliases();
}
// you can rebind an alias, but only to the initially-aliased namespace.
if(!map.valAt(alias).equals(ns))
throw new IllegalStateException("Alias " + alias + " already exists in namespace "
+ name + ", aliasing " + map.valAt(alias));
}
public void removeAlias(Symbol alias) {
IPersistentMap map = getAliases();
while(map.containsKey(alias))
{
IPersistentMap newMap = map.without(alias);
aliases.compareAndSet(map, newMap);
map = getAliases();
}
}
private Object readResolve() throws ObjectStreamException {
// ensures that serialized namespaces are "deserialized" to the
// namespace in the present runtime
return findOrCreate(name);
}
}
================================================
FILE: src/jvm/clojure/lang/Numbers.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 31, 2008 */
package clojure.lang;
import java.math.BigInteger;
import java.math.BigDecimal;
import java.math.MathContext;
public class Numbers{
static interface Ops{
Ops combine(Ops y);
Ops opsWith(LongOps x);
Ops opsWith(DoubleOps x);
Ops opsWith(RatioOps x);
Ops opsWith(BigIntOps x);
Ops opsWith(BigDecimalOps x);
public boolean isZero(Number x);
public boolean isPos(Number x);
public boolean isNeg(Number x);
public Number add(Number x, Number y);
public Number addP(Number x, Number y);
public Number multiply(Number x, Number y);
public Number multiplyP(Number x, Number y);
public Number divide(Number x, Number y);
public Number quotient(Number x, Number y);
public Number remainder(Number x, Number y);
public boolean equiv(Number x, Number y);
public boolean lt(Number x, Number y);
public boolean lte(Number x, Number y);
public boolean gte(Number x, Number y);
public Number negate(Number x);
public Number negateP(Number x);
public Number inc(Number x);
public Number incP(Number x);
public Number dec(Number x);
public Number decP(Number x);
}
static abstract class OpsP implements Ops{
public Number addP(Number x, Number y){
return add(x, y);
}
public Number multiplyP(Number x, Number y){
return multiply(x, y);
}
public Number negateP(Number x){
return negate(x);
}
public Number incP(Number x){
return inc(x);
}
public Number decP(Number x){
return dec(x);
}
}
static public boolean isZero(Object x){
return ops(x).isZero((Number)x);
}
static public boolean isPos(Object x){
return ops(x).isPos((Number)x);
}
static public boolean isNeg(Object x){
return ops(x).isNeg((Number)x);
}
static public Number minus(Object x){
return ops(x).negate((Number)x);
}
static public Number minusP(Object x){
return ops(x).negateP((Number)x);
}
static public Number inc(Object x){
return ops(x).inc((Number)x);
}
static public Number incP(Object x){
return ops(x).incP((Number)x);
}
static public Number dec(Object x){
return ops(x).dec((Number)x);
}
static public Number decP(Object x){
return ops(x).decP((Number)x);
}
static public Number add(Object x, Object y){
return ops(x).combine(ops(y)).add((Number)x, (Number)y);
}
static public Number addP(Object x, Object y){
return ops(x).combine(ops(y)).addP((Number)x, (Number)y);
}
static public Number minus(Object x, Object y){
Ops yops = ops(y);
return ops(x).combine(yops).add((Number)x, yops.negate((Number)y));
}
static public Number minusP(Object x, Object y){
Ops yops = ops(y);
Number negativeY = yops.negateP((Number) y);
Ops negativeYOps = ops(negativeY);
return ops(x).combine(negativeYOps).addP((Number)x, negativeY);
}
static public Number multiply(Object x, Object y){
return ops(x).combine(ops(y)).multiply((Number)x, (Number)y);
}
static public Number multiplyP(Object x, Object y){
return ops(x).combine(ops(y)).multiplyP((Number)x, (Number)y);
}
static public Number divide(Object x, Object y){
Ops yops = ops(y);
if(yops.isZero((Number)y))
throw new ArithmeticException("Divide by zero");
return ops(x).combine(yops).divide((Number)x, (Number)y);
}
static public Number quotient(Object x, Object y){
Ops yops = ops(y);
if(yops.isZero((Number) y))
throw new ArithmeticException("Divide by zero");
return ops(x).combine(yops).quotient((Number)x, (Number)y);
}
static public Number remainder(Object x, Object y){
Ops yops = ops(y);
if(yops.isZero((Number) y))
throw new ArithmeticException("Divide by zero");
return ops(x).combine(yops).remainder((Number)x, (Number)y);
}
static public double quotient(double n, double d){
if(d == 0)
throw new ArithmeticException("Divide by zero");
double q = n / d;
if(q <= Long.MAX_VALUE && q >= Long.MIN_VALUE)
{
return (double)(long) q;
}
else
{ //bigint quotient
return new BigDecimal(q).toBigInteger().doubleValue();
}
}
static public double remainder(double n, double d){
if(d == 0)
throw new ArithmeticException("Divide by zero");
double q = n / d;
if(q <= Long.MAX_VALUE && q >= Long.MIN_VALUE)
{
return (n - ((long) q) * d);
}
else
{ //bigint quotient
Number bq = new BigDecimal(q).toBigInteger();
return (n - bq.doubleValue() * d);
}
}
static public boolean equiv(Object x, Object y){
return equiv((Number) x, (Number) y);
}
static public boolean equiv(Number x, Number y){
return ops(x).combine(ops(y)).equiv(x, y);
}
static public boolean equal(Number x, Number y){
return category(x) == category(y)
&& ops(x).combine(ops(y)).equiv(x, y);
}
static public boolean lt(Object x, Object y){
return ops(x).combine(ops(y)).lt((Number)x, (Number)y);
}
static public boolean lte(Object x, Object y){
return ops(x).combine(ops(y)).lte((Number)x, (Number)y);
}
static public boolean gt(Object x, Object y){
return ops(x).combine(ops(y)).lt((Number)y, (Number)x);
}
static public boolean gte(Object x, Object y){
return ops(x).combine(ops(y)).gte((Number)x, (Number)y);
}
static public int compare(Number x, Number y){
Ops ops = ops(x).combine(ops(y));
if(ops.lt(x, y))
return -1;
else if(ops.lt(y, x))
return 1;
return 0;
}
@WarnBoxedMath(false)
static BigInt toBigInt(Object x){
if(x instanceof BigInt)
return (BigInt) x;
if(x instanceof BigInteger)
return BigInt.fromBigInteger((BigInteger) x);
else
return BigInt.fromLong(((Number) x).longValue());
}
@WarnBoxedMath(false)
static BigInteger toBigInteger(Object x){
if(x instanceof BigInteger)
return (BigInteger) x;
else if(x instanceof BigInt)
return ((BigInt) x).toBigInteger();
else
return BigInteger.valueOf(((Number) x).longValue());
}
@WarnBoxedMath(false)
static BigDecimal toBigDecimal(Object x){
if(x instanceof BigDecimal)
return (BigDecimal) x;
else if(x instanceof BigInt)
{
BigInt bi = (BigInt) x;
if(bi.bipart == null)
return BigDecimal.valueOf(bi.lpart);
else
return new BigDecimal(bi.bipart);
}
else if(x instanceof BigInteger)
return new BigDecimal((BigInteger) x);
else if(x instanceof Double)
return new BigDecimal(((Number) x).doubleValue());
else if(x instanceof Float)
return new BigDecimal(((Number) x).doubleValue());
else if(x instanceof Ratio)
{
Ratio r = (Ratio)x;
return (BigDecimal)divide(new BigDecimal(r.numerator), r.denominator);
}
else
return BigDecimal.valueOf(((Number) x).longValue());
}
@WarnBoxedMath(false)
static public Ratio toRatio(Object x){
if(x instanceof Ratio)
return (Ratio) x;
else if(x instanceof BigDecimal)
{
BigDecimal bx = (BigDecimal) x;
BigInteger bv = bx.unscaledValue();
int scale = bx.scale();
if(scale < 0)
return new Ratio(bv.multiply(BigInteger.TEN.pow(-scale)), BigInteger.ONE);
else
return new Ratio(bv, BigInteger.TEN.pow(scale));
}
return new Ratio(toBigInteger(x), BigInteger.ONE);
}
@WarnBoxedMath(false)
static public Number rationalize(Number x){
if(x instanceof Float || x instanceof Double)
return rationalize(BigDecimal.valueOf(x.doubleValue()));
else if(x instanceof BigDecimal)
{
BigDecimal bx = (BigDecimal) x;
BigInteger bv = bx.unscaledValue();
int scale = bx.scale();
if(scale < 0)
return BigInt.fromBigInteger(bv.multiply(BigInteger.TEN.pow(-scale)));
else
return divide(bv, BigInteger.TEN.pow(scale));
}
return x;
}
//static Number box(int val){
// return Integer.valueOf(val);
//}
//static Number box(long val){
// return Long.valueOf(val);
//}
//
//static Double box(double val){
// return Double.valueOf(val);
//}
//
//static Double box(float val){
// return Double.valueOf((double) val);
//}
@WarnBoxedMath(false)
static public Number reduceBigInt(BigInt val){
if(val.bipart == null)
return num(val.lpart);
else
return val.bipart;
}
static public Number divide(BigInteger n, BigInteger d){
if(d.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by zero");
BigInteger gcd = n.gcd(d);
if(gcd.equals(BigInteger.ZERO))
return BigInt.ZERO;
n = n.divide(gcd);
d = d.divide(gcd);
if(d.equals(BigInteger.ONE))
return BigInt.fromBigInteger(n);
else if(d.equals(BigInteger.ONE.negate()))
return BigInt.fromBigInteger(n.negate());
return new Ratio((d.signum() < 0 ? n.negate() : n),
(d.signum() < 0 ? d.negate() : d));
}
static public int shiftLeftInt(int x, int n){
return x << n;
}
static public long shiftLeft(Object x, Object y){
return shiftLeft(bitOpsCast(x),bitOpsCast(y));
}
static public long shiftLeft(Object x, long y){
return shiftLeft(bitOpsCast(x),y);
}
static public long shiftLeft(long x, Object y){
return shiftLeft(x,bitOpsCast(y));
}
static public long shiftLeft(long x, long n){
return x << n;
}
static public int shiftRightInt(int x, int n){
return x >> n;
}
static public long shiftRight(Object x, Object y){
return shiftRight(bitOpsCast(x),bitOpsCast(y));
}
static public long shiftRight(Object x, long y){
return shiftRight(bitOpsCast(x),y);
}
static public long shiftRight(long x, Object y){
return shiftRight(x,bitOpsCast(y));
}
static public long shiftRight(long x, long n){
return x >> n;
}
static public int unsignedShiftRightInt(int x, int n){
return x >>> n;
}
static public long unsignedShiftRight(Object x, Object y){
return unsignedShiftRight(bitOpsCast(x),bitOpsCast(y));
}
static public long unsignedShiftRight(Object x, long y){
return unsignedShiftRight(bitOpsCast(x),y);
}
static public long unsignedShiftRight(long x, Object y){
return unsignedShiftRight(x,bitOpsCast(y));
}
static public long unsignedShiftRight(long x, long n){
return x >>> n;
}
final static class LongOps implements Ops{
public Ops combine(Ops y){
return y.opsWith(this);
}
final public Ops opsWith(LongOps x){
return this;
}
final public Ops opsWith(DoubleOps x){
return DOUBLE_OPS;
}
final public Ops opsWith(RatioOps x){
return RATIO_OPS;
}
final public Ops opsWith(BigIntOps x){
return BIGINT_OPS;
}
final public Ops opsWith(BigDecimalOps x){
return BIGDECIMAL_OPS;
}
public boolean isZero(Number x){
return x.longValue() == 0;
}
public boolean isPos(Number x){
return x.longValue() > 0;
}
public boolean isNeg(Number x){
return x.longValue() < 0;
}
final public Number add(Number x, Number y){
return num(Numbers.add(x.longValue(),y.longValue()));
}
final public Number addP(Number x, Number y){
long lx = x.longValue(), ly = y.longValue();
long ret = lx + ly;
if ((ret ^ lx) < 0 && (ret ^ ly) < 0)
return BIGINT_OPS.add(x, y);
return num(ret);
}
final public Number multiply(Number x, Number y){
return num(Numbers.multiply(x.longValue(), y.longValue()));
}
final public Number multiplyP(Number x, Number y){
long lx = x.longValue(), ly = y.longValue();
if (lx == Long.MIN_VALUE && ly < 0)
return BIGINT_OPS.multiply(x, y);
long ret = lx * ly;
if (ly != 0 && ret/ly != lx)
return BIGINT_OPS.multiply(x, y);
return num(ret);
}
static long gcd(long u, long v){
while(v != 0)
{
long r = u % v;
u = v;
v = r;
}
return u;
}
public Number divide(Number x, Number y){
long n = x.longValue();
long val = y.longValue();
long gcd = gcd(n, val);
if(gcd == 0)
return num(0);
n = n / gcd;
long d = val / gcd;
if(d == 1)
return num(n);
if(d < 0)
{
n = -n;
d = -d;
}
return new Ratio(BigInteger.valueOf(n), BigInteger.valueOf(d));
}
public Number quotient(Number x, Number y){
return num(x.longValue() / y.longValue());
}
public Number remainder(Number x, Number y){
return num(x.longValue() % y.longValue());
}
public boolean equiv(Number x, Number y){
return x.longValue() == y.longValue();
}
public boolean lt(Number x, Number y){
return x.longValue() < y.longValue();
}
public boolean lte(Number x, Number y){
return x.longValue() <= y.longValue();
}
public boolean gte(Number x, Number y){
return x.longValue() >= y.longValue();
}
//public Number subtract(Number x, Number y);
final public Number negate(Number x){
long val = x.longValue();
return num(Numbers.minus(val));
}
final public Number negateP(Number x){
long val = x.longValue();
if(val > Long.MIN_VALUE)
return num(-val);
return BigInt.fromBigInteger(BigInteger.valueOf(val).negate());
}
public Number inc(Number x){
long val = x.longValue();
return num(Numbers.inc(val));
}
public Number incP(Number x){
long val = x.longValue();
if(val < Long.MAX_VALUE)
return num(val + 1);
return BIGINT_OPS.inc(x);
}
public Number dec(Number x){
long val = x.longValue();
return num(Numbers.dec(val));
}
public Number decP(Number x){
long val = x.longValue();
if(val > Long.MIN_VALUE)
return num(val - 1);
return BIGINT_OPS.dec(x);
}
}
final static class DoubleOps extends OpsP{
public Ops combine(Ops y){
return y.opsWith(this);
}
final public Ops opsWith(LongOps x){
return this;
}
final public Ops opsWith(DoubleOps x){
return this;
}
final public Ops opsWith(RatioOps x){
return this;
}
final public Ops opsWith(BigIntOps x){
return this;
}
final public Ops opsWith(BigDecimalOps x){
return this;
}
public boolean isZero(Number x){
return x.doubleValue() == 0;
}
public boolean isPos(Number x){
return x.doubleValue() > 0;
}
public boolean isNeg(Number x){
return x.doubleValue() < 0;
}
final public Number add(Number x, Number y){
return Double.valueOf(x.doubleValue() + y.doubleValue());
}
final public Number multiply(Number x, Number y){
return Double.valueOf(x.doubleValue() * y.doubleValue());
}
public Number divide(Number x, Number y){
return Double.valueOf(x.doubleValue() / y.doubleValue());
}
public Number quotient(Number x, Number y){
return Numbers.quotient(x.doubleValue(), y.doubleValue());
}
public Number remainder(Number x, Number y){
return Numbers.remainder(x.doubleValue(), y.doubleValue());
}
public boolean equiv(Number x, Number y){
return x.doubleValue() == y.doubleValue();
}
public boolean lt(Number x, Number y){
return x.doubleValue() < y.doubleValue();
}
public boolean lte(Number x, Number y){
return x.doubleValue() <= y.doubleValue();
}
public boolean gte(Number x, Number y){
return x.doubleValue() >= y.doubleValue();
}
//public Number subtract(Number x, Number y);
final public Number negate(Number x){
return Double.valueOf(-x.doubleValue());
}
public Number inc(Number x){
return Double.valueOf(x.doubleValue() + 1);
}
public Number dec(Number x){
return Double.valueOf(x.doubleValue() - 1);
}
}
final static class RatioOps extends OpsP{
public Ops combine(Ops y){
return y.opsWith(this);
}
final public Ops opsWith(LongOps x){
return this;
}
final public Ops opsWith(DoubleOps x){
return DOUBLE_OPS;
}
final public Ops opsWith(RatioOps x){
return this;
}
final public Ops opsWith(BigIntOps x){
return this;
}
final public Ops opsWith(BigDecimalOps x){
return BIGDECIMAL_OPS;
}
public boolean isZero(Number x){
Ratio r = (Ratio) x;
return r.numerator.signum() == 0;
}
public boolean isPos(Number x){
Ratio r = (Ratio) x;
return r.numerator.signum() > 0;
}
public boolean isNeg(Number x){
Ratio r = (Ratio) x;
return r.numerator.signum() < 0;
}
static Number normalizeRet(Number ret, Number x, Number y){
// if(ret instanceof BigInteger && !(x instanceof BigInteger || y instanceof BigInteger))
// {
// return reduceBigInt((BigInteger) ret);
// }
return ret;
}
final public Number add(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
Number ret = divide(ry.numerator.multiply(rx.denominator)
.add(rx.numerator.multiply(ry.denominator))
, ry.denominator.multiply(rx.denominator));
return normalizeRet(ret, x, y);
}
final public Number multiply(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
Number ret = Numbers.divide(ry.numerator.multiply(rx.numerator)
, ry.denominator.multiply(rx.denominator));
return normalizeRet(ret, x, y);
}
public Number divide(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
Number ret = Numbers.divide(ry.denominator.multiply(rx.numerator)
, ry.numerator.multiply(rx.denominator));
return normalizeRet(ret, x, y);
}
public Number quotient(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
BigInteger q = rx.numerator.multiply(ry.denominator).divide(
rx.denominator.multiply(ry.numerator));
return normalizeRet(BigInt.fromBigInteger(q), x, y);
}
public Number remainder(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
BigInteger q = rx.numerator.multiply(ry.denominator).divide(
rx.denominator.multiply(ry.numerator));
Number ret = Numbers.minus(x, Numbers.multiply(q, y));
return normalizeRet(ret, x, y);
}
public boolean equiv(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
return rx.numerator.equals(ry.numerator)
&& rx.denominator.equals(ry.denominator);
}
public boolean lt(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
return Numbers.lt(rx.numerator.multiply(ry.denominator), ry.numerator.multiply(rx.denominator));
}
public boolean lte(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
return Numbers.lte(rx.numerator.multiply(ry.denominator), ry.numerator.multiply(rx.denominator));
}
public boolean gte(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
return Numbers.gte(rx.numerator.multiply(ry.denominator), ry.numerator.multiply(rx.denominator));
}
//public Number subtract(Number x, Number y);
final public Number negate(Number x){
Ratio r = (Ratio) x;
return new Ratio(r.numerator.negate(), r.denominator);
}
public Number inc(Number x){
return Numbers.add(x, 1);
}
public Number dec(Number x){
return Numbers.add(x, -1);
}
}
final static class BigIntOps extends OpsP{
public Ops combine(Ops y){
return y.opsWith(this);
}
final public Ops opsWith(LongOps x){
return this;
}
final public Ops opsWith(DoubleOps x){
return DOUBLE_OPS;
}
final public Ops opsWith(RatioOps x){
return RATIO_OPS;
}
final public Ops opsWith(BigIntOps x){
return this;
}
final public Ops opsWith(BigDecimalOps x){
return BIGDECIMAL_OPS;
}
public boolean isZero(Number x){
BigInt bx = toBigInt(x);
if(bx.bipart == null)
return bx.lpart == 0;
return bx.bipart.signum() == 0;
}
public boolean isPos(Number x){
BigInt bx = toBigInt(x);
if(bx.bipart == null)
return bx.lpart > 0;
return bx.bipart.signum() > 0;
}
public boolean isNeg(Number x){
BigInt bx = toBigInt(x);
if(bx.bipart == null)
return bx.lpart < 0;
return bx.bipart.signum() < 0;
}
final public Number add(Number x, Number y){
return toBigInt(x).add(toBigInt(y));
}
final public Number multiply(Number x, Number y){
return toBigInt(x).multiply(toBigInt(y));
}
public Number divide(Number x, Number y){
return Numbers.divide(toBigInteger(x), toBigInteger(y));
}
public Number quotient(Number x, Number y){
return toBigInt(x).quotient(toBigInt(y));
}
public Number remainder(Number x, Number y){
return toBigInt(x).remainder(toBigInt(y));
}
public boolean equiv(Number x, Number y){
return toBigInt(x).equals(toBigInt(y));
}
public boolean lt(Number x, Number y){
return toBigInt(x).lt(toBigInt(y));
}
public boolean lte(Number x, Number y){
return toBigInteger(x).compareTo(toBigInteger(y)) <= 0;
}
public boolean gte(Number x, Number y){
return toBigInteger(x).compareTo(toBigInteger(y)) >= 0;
}
//public Number subtract(Number x, Number y);
final public Number negate(Number x){
return BigInt.fromBigInteger(toBigInteger(x).negate());
}
public Number inc(Number x){
BigInteger bx = toBigInteger(x);
return BigInt.fromBigInteger(bx.add(BigInteger.ONE));
}
public Number dec(Number x){
BigInteger bx = toBigInteger(x);
return BigInt.fromBigInteger(bx.subtract(BigInteger.ONE));
}
}
final static class BigDecimalOps extends OpsP{
final static Var MATH_CONTEXT = RT.MATH_CONTEXT;
public Ops combine(Ops y){
return y.opsWith(this);
}
final public Ops opsWith(LongOps x){
return this;
}
final public Ops opsWith(DoubleOps x){
return DOUBLE_OPS;
}
final public Ops opsWith(RatioOps x){
return this;
}
final public Ops opsWith(BigIntOps x){
return this;
}
final public Ops opsWith(BigDecimalOps x){
return this;
}
public boolean isZero(Number x){
BigDecimal bx = (BigDecimal) x;
return bx.signum() == 0;
}
public boolean isPos(Number x){
BigDecimal bx = (BigDecimal) x;
return bx.signum() > 0;
}
public boolean isNeg(Number x){
BigDecimal bx = (BigDecimal) x;
return bx.signum() < 0;
}
final public Number add(Number x, Number y){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
return mc == null
? toBigDecimal(x).add(toBigDecimal(y))
: toBigDecimal(x).add(toBigDecimal(y), mc);
}
final public Number multiply(Number x, Number y){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
return mc == null
? toBigDecimal(x).multiply(toBigDecimal(y))
: toBigDecimal(x).multiply(toBigDecimal(y), mc);
}
public Number divide(Number x, Number y){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
return mc == null
? toBigDecimal(x).divide(toBigDecimal(y))
: toBigDecimal(x).divide(toBigDecimal(y), mc);
}
public Number quotient(Number x, Number y){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
return mc == null
? toBigDecimal(x).divideToIntegralValue(toBigDecimal(y))
: toBigDecimal(x).divideToIntegralValue(toBigDecimal(y), mc);
}
public Number remainder(Number x, Number y){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
return mc == null
? toBigDecimal(x).remainder(toBigDecimal(y))
: toBigDecimal(x).remainder(toBigDecimal(y), mc);
}
public boolean equiv(Number x, Number y){
return toBigDecimal(x).compareTo(toBigDecimal(y)) == 0;
}
public boolean lt(Number x, Number y){
return toBigDecimal(x).compareTo(toBigDecimal(y)) < 0;
}
public boolean lte(Number x, Number y){
return toBigDecimal(x).compareTo(toBigDecimal(y)) <= 0;
}
public boolean gte(Number x, Number y){
return toBigDecimal(x).compareTo(toBigDecimal(y)) >= 0;
}
//public Number subtract(Number x, Number y);
final public Number negate(Number x){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
return mc == null
? ((BigDecimal) x).negate()
: ((BigDecimal) x).negate(mc);
}
public Number inc(Number x){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
BigDecimal bx = (BigDecimal) x;
return mc == null
? bx.add(BigDecimal.ONE)
: bx.add(BigDecimal.ONE, mc);
}
public Number dec(Number x){
MathContext mc = (MathContext) MATH_CONTEXT.deref();
BigDecimal bx = (BigDecimal) x;
return mc == null
? bx.subtract(BigDecimal.ONE)
: bx.subtract(BigDecimal.ONE, mc);
}
}
static final LongOps LONG_OPS = new LongOps();
static final DoubleOps DOUBLE_OPS = new DoubleOps();
static final RatioOps RATIO_OPS = new RatioOps();
static final BigIntOps BIGINT_OPS = new BigIntOps();
static final BigDecimalOps BIGDECIMAL_OPS = new BigDecimalOps();
static public enum Category {INTEGER, FLOATING, DECIMAL, RATIO};
static Ops ops(Object x){
Class xc = x.getClass();
if(xc == Long.class)
return LONG_OPS;
else if(xc == Double.class)
return DOUBLE_OPS;
else if(xc == Integer.class)
return LONG_OPS;
else if(xc == Float.class)
return DOUBLE_OPS;
else if(xc == BigInt.class)
return BIGINT_OPS;
else if(xc == BigInteger.class)
return BIGINT_OPS;
else if(xc == Ratio.class)
return RATIO_OPS;
else if(xc == BigDecimal.class)
return BIGDECIMAL_OPS;
else
return LONG_OPS;
}
@WarnBoxedMath(false)
static int hasheq(Number x){
Class xc = x.getClass();
if(xc == Long.class
|| xc == Integer.class
|| xc == Short.class
|| xc == Byte.class
|| (xc == BigInteger.class && lte(x, Long.MAX_VALUE) && gte(x,Long.MIN_VALUE)))
{
long lpart = x.longValue();
return Murmur3.hashLong(lpart);
//return (int) (lpart ^ (lpart >>> 32));
}
if(xc == BigDecimal.class)
{
// stripTrailingZeros() to make all numerically equal
// BigDecimal values come out the same before calling
// hashCode. Special check for 0 because
// stripTrailingZeros() does not do anything to values
// equal to 0 with different scales.
if (isZero(x))
return Util.hash(BigDecimal.ZERO);
else
{
BigDecimal tmp = ((BigDecimal) x).stripTrailingZeros();
return Util.hash(tmp);
}
}
return Util.hash(x);
}
static Category category(Object x){
Class xc = x.getClass();
if(xc == Integer.class)
return Category.INTEGER;
else if(xc == Double.class)
return Category.FLOATING;
else if(xc == Long.class)
return Category.INTEGER;
else if(xc == Float.class)
return Category.FLOATING;
else if(xc == BigInt.class)
return Category.INTEGER;
else if(xc == Ratio.class)
return Category.RATIO;
else if(xc == BigDecimal.class)
return Category.DECIMAL;
else
return Category.INTEGER;
}
static long bitOpsCast(Object x){
Class xc = x.getClass();
if(xc == Long.class
|| xc == Integer.class
|| xc == Short.class
|| xc == Byte.class)
return RT.longCast(x);
// no bignums, no decimals
throw new IllegalArgumentException("bit operation not supported for: " + xc);
}
@WarnBoxedMath(false)
static public float[] float_array(int size, Object init){
float[] ret = new float[size];
if(init instanceof Number)
{
float f = ((Number) init).floatValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).floatValue();
}
return ret;
}
@WarnBoxedMath(false)
static public float[] float_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new float[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
float[] ret = new float[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).floatValue();
return ret;
}
}
@WarnBoxedMath(false)
static public double[] double_array(int size, Object init){
double[] ret = new double[size];
if(init instanceof Number)
{
double f = ((Number) init).doubleValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).doubleValue();
}
return ret;
}
@WarnBoxedMath(false)
static public double[] double_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new double[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
double[] ret = new double[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).doubleValue();
return ret;
}
}
@WarnBoxedMath(false)
static public int[] int_array(int size, Object init){
int[] ret = new int[size];
if(init instanceof Number)
{
int f = ((Number) init).intValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).intValue();
}
return ret;
}
@WarnBoxedMath(false)
static public int[] int_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new int[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
int[] ret = new int[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).intValue();
return ret;
}
}
@WarnBoxedMath(false)
static public long[] long_array(int size, Object init){
long[] ret = new long[size];
if(init instanceof Number)
{
long f = ((Number) init).longValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).longValue();
}
return ret;
}
@WarnBoxedMath(false)
static public long[] long_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new long[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
long[] ret = new long[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).longValue();
return ret;
}
}
@WarnBoxedMath(false)
static public short[] short_array(int size, Object init){
short[] ret = new short[size];
if(init instanceof Short)
{
short s = (Short) init;
for(int i = 0; i < ret.length; i++)
ret[i] = s;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).shortValue();
}
return ret;
}
@WarnBoxedMath(false)
static public short[] short_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new short[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
short[] ret = new short[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).shortValue();
return ret;
}
}
@WarnBoxedMath(false)
static public char[] char_array(int size, Object init){
char[] ret = new char[size];
if(init instanceof Character)
{
char c = (Character) init;
for(int i = 0; i < ret.length; i++)
ret[i] = c;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = (Character) s.first();
}
return ret;
}
@WarnBoxedMath(false)
static public char[] char_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new char[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
char[] ret = new char[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = (Character) s.first();
return ret;
}
}
@WarnBoxedMath(false)
static public byte[] byte_array(int size, Object init){
byte[] ret = new byte[size];
if(init instanceof Byte)
{
byte b = (Byte) init;
for(int i = 0; i < ret.length; i++)
ret[i] = b;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).byteValue();
}
return ret;
}
@WarnBoxedMath(false)
static public byte[] byte_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new byte[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
byte[] ret = new byte[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = ((Number) s.first()).byteValue();
return ret;
}
}
@WarnBoxedMath(false)
static public boolean[] boolean_array(int size, Object init){
boolean[] ret = new boolean[size];
if(init instanceof Boolean)
{
boolean b = (Boolean) init;
for(int i = 0; i < ret.length; i++)
ret[i] = b;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = (Boolean)s.first();
}
return ret;
}
@WarnBoxedMath(false)
static public boolean[] boolean_array(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new boolean[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
boolean[] ret = new boolean[size];
for(int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = (Boolean)s.first();
return ret;
}
}
@WarnBoxedMath(false)
static public boolean[] booleans(Object array){
return (boolean[]) array;
}
@WarnBoxedMath(false)
static public byte[] bytes(Object array){
return (byte[]) array;
}
@WarnBoxedMath(false)
static public char[] chars(Object array){
return (char[]) array;
}
@WarnBoxedMath(false)
static public short[] shorts(Object array){
return (short[]) array;
}
@WarnBoxedMath(false)
static public float[] floats(Object array){
return (float[]) array;
}
@WarnBoxedMath(false)
static public double[] doubles(Object array){
return (double[]) array;
}
@WarnBoxedMath(false)
static public int[] ints(Object array){
return (int[]) array;
}
@WarnBoxedMath(false)
static public long[] longs(Object array){
return (long[]) array;
}
static public Number num(Object x){
return (Number) x;
}
static public Number num(float x){
return Float.valueOf(x);
}
static public Number num(double x){
return Double.valueOf(x);
}
static public double add(double x, double y){
return x + y;
}
static public double addP(double x, double y){
return x + y;
}
static public double minus(double x, double y){
return x - y;
}
static public double minusP(double x, double y){
return x - y;
}
static public double minus(double x){
return -x;
}
static public double minusP(double x){
return -x;
}
static public double inc(double x){
return x + 1;
}
static public double incP(double x){
return x + 1;
}
static public double dec(double x){
return x - 1;
}
static public double decP(double x){
return x - 1;
}
static public double multiply(double x, double y){
return x * y;
}
static public double multiplyP(double x, double y){
return x * y;
}
static public double divide(double x, double y){
return x / y;
}
static public boolean equiv(double x, double y){
return x == y;
}
static public boolean lt(double x, double y){
return x < y;
}
static public boolean lte(double x, double y){
return x <= y;
}
static public boolean gt(double x, double y){
return x > y;
}
static public boolean gte(double x, double y){
return x >= y;
}
static public boolean isPos(double x){
return x > 0;
}
static public boolean isNeg(double x){
return x < 0;
}
static public boolean isZero(double x){
return x == 0;
}
static int throwIntOverflow(){
throw new ArithmeticException("integer overflow");
}
//static public Number num(int x){
// return Integer.valueOf(x);
//}
static public int unchecked_int_add(int x, int y){
return x + y;
}
static public int unchecked_int_subtract(int x, int y){
return x - y;
}
static public int unchecked_int_negate(int x){
return -x;
}
static public int unchecked_int_inc(int x){
return x + 1;
}
static public int unchecked_int_dec(int x){
return x - 1;
}
static public int unchecked_int_multiply(int x, int y){
return x * y;
}
//static public int add(int x, int y){
// int ret = x + y;
// if ((ret ^ x) < 0 && (ret ^ y) < 0)
// return throwIntOverflow();
// return ret;
//}
//static public int not(int x){
// return ~x;
//}
static public long not(Object x){
return not(bitOpsCast(x));
}
static public long not(long x){
return ~x;
}
//static public int and(int x, int y){
// return x & y;
//}
static public long and(Object x, Object y){
return and(bitOpsCast(x),bitOpsCast(y));
}
static public long and(Object x, long y){
return and(bitOpsCast(x),y);
}
static public long and(long x, Object y){
return and(x,bitOpsCast(y));
}
static public long and(long x, long y){
return x & y;
}
//static public int or(int x, int y){
// return x | y;
//}
static public long or(Object x, Object y){
return or(bitOpsCast(x),bitOpsCast(y));
}
static public long or(Object x, long y){
return or(bitOpsCast(x),y);
}
static public long or(long x, Object y){
return or(x,bitOpsCast(y));
}
static public long or(long x, long y){
return x | y;
}
//static public int xor(int x, int y){
// return x ^ y;
//}
static public long xor(Object x, Object y){
return xor(bitOpsCast(x),bitOpsCast(y));
}
static public long xor(Object x, long y){
return xor(bitOpsCast(x),y);
}
static public long xor(long x, Object y){
return xor(x,bitOpsCast(y));
}
static public long xor(long x, long y){
return x ^ y;
}
static public long andNot(Object x, Object y){
return andNot(bitOpsCast(x),bitOpsCast(y));
}
static public long andNot(Object x, long y){
return andNot(bitOpsCast(x),y);
}
static public long andNot(long x, Object y){
return andNot(x,bitOpsCast(y));
}
static public long andNot(long x, long y){
return x & ~y;
}
static public long clearBit(Object x, Object y){
return clearBit(bitOpsCast(x),bitOpsCast(y));
}
static public long clearBit(Object x, long y){
return clearBit(bitOpsCast(x),y);
}
static public long clearBit(long x, Object y){
return clearBit(x,bitOpsCast(y));
}
static public long clearBit(long x, long n){
return x & ~(1L << n);
}
static public long setBit(Object x, Object y){
return setBit(bitOpsCast(x),bitOpsCast(y));
}
static public long setBit(Object x, long y){
return setBit(bitOpsCast(x),y);
}
static public long setBit(long x, Object y){
return setBit(x,bitOpsCast(y));
}
static public long setBit(long x, long n){
return x | (1L << n);
}
static public long flipBit(Object x, Object y){
return flipBit(bitOpsCast(x),bitOpsCast(y));
}
static public long flipBit(Object x, long y){
return flipBit(bitOpsCast(x),y);
}
static public long flipBit(long x, Object y){
return flipBit(x,bitOpsCast(y));
}
static public long flipBit(long x, long n){
return x ^ (1L << n);
}
static public boolean testBit(Object x, Object y){
return testBit(bitOpsCast(x),bitOpsCast(y));
}
static public boolean testBit(Object x, long y){
return testBit(bitOpsCast(x),y);
}
static public boolean testBit(long x, Object y){
return testBit(x,bitOpsCast(y));
}
static public boolean testBit(long x, long n){
return (x & (1L << n)) != 0;
}
//static public int minus(int x, int y){
// int ret = x - y;
// if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
// return throwIntOverflow();
// return ret;
//}
//static public int minus(int x){
// if(x == Integer.MIN_VALUE)
// return throwIntOverflow();
// return -x;
//}
//static public int inc(int x){
// if(x == Integer.MAX_VALUE)
// return throwIntOverflow();
// return x + 1;
//}
//static public int dec(int x){
// if(x == Integer.MIN_VALUE)
// return throwIntOverflow();
// return x - 1;
//}
//static public int multiply(int x, int y){
// int ret = x * y;
// if (y != 0 && ret/y != x)
// return throwIntOverflow();
// return ret;
//}
static public int unchecked_int_divide(int x, int y){
return x / y;
}
static public int unchecked_int_remainder(int x, int y){
return x % y;
}
//static public boolean equiv(int x, int y){
// return x == y;
//}
//static public boolean lt(int x, int y){
// return x < y;
//}
//static public boolean lte(int x, int y){
// return x <= y;
//}
//static public boolean gt(int x, int y){
// return x > y;
//}
//static public boolean gte(int x, int y){
// return x >= y;
//}
//static public boolean isPos(int x){
// return x > 0;
//}
//static public boolean isNeg(int x){
// return x < 0;
//}
//static public boolean isZero(int x){
// return x == 0;
//}
static public Number num(long x){
return Long.valueOf(x);
}
static public long unchecked_add(long x, long y){return x + y;}
static public long unchecked_minus(long x, long y){return x - y;}
static public long unchecked_multiply(long x, long y){return x * y;}
static public long unchecked_minus(long x){return -x;}
static public long unchecked_inc(long x){return x + 1;}
static public long unchecked_dec(long x){return x - 1;}
static public Number unchecked_add(Object x, Object y){return add(x,y);}
static public Number unchecked_minus(Object x, Object y){return minus(x,y);}
static public Number unchecked_multiply(Object x, Object y){return multiply(x,y);}
static public Number unchecked_minus(Object x){return minus(x);}
static public Number unchecked_inc(Object x){return inc(x);}
static public Number unchecked_dec(Object x){return dec(x);}
static public double unchecked_add(double x, double y){return add(x,y);}
static public double unchecked_minus(double x, double y){return minus(x,y);}
static public double unchecked_multiply(double x, double y){return multiply(x,y);}
static public double unchecked_minus(double x){return minus(x);}
static public double unchecked_inc(double x){return inc(x);}
static public double unchecked_dec(double x){return dec(x);}
static public double unchecked_add(double x, Object y){return add(x,y);}
static public double unchecked_minus(double x, Object y){return minus(x,y);}
static public double unchecked_multiply(double x, Object y){return multiply(x,y);}
static public double unchecked_add(Object x, double y){return add(x,y);}
static public double unchecked_minus(Object x, double y){return minus(x,y);}
static public double unchecked_multiply(Object x, double y){return multiply(x,y);}
static public double unchecked_add(double x, long y){return add(x,y);}
static public double unchecked_minus(double x, long y){return minus(x,y);}
static public double unchecked_multiply(double x, long y){return multiply(x,y);}
static public double unchecked_add(long x, double y){return add(x,y);}
static public double unchecked_minus(long x, double y){return minus(x,y);}
static public double unchecked_multiply(long x, double y){return multiply(x,y);}
static public Number unchecked_add(long x, Object y){return add(x,y);}
static public Number unchecked_minus(long x, Object y){return minus(x,y);}
static public Number unchecked_multiply(long x, Object y){return multiply(x,y);}
static public Number unchecked_add(Object x, long y){return add(x,y);}
static public Number unchecked_minus(Object x, long y){return minus(x,y);}
static public Number unchecked_multiply(Object x, long y){return multiply(x,y);}
static public Number quotient(double x, Object y){return quotient((Object)x,y);}
static public Number quotient(Object x, double y){return quotient(x,(Object)y);}
static public Number quotient(long x, Object y){return quotient((Object)x,y);}
static public Number quotient(Object x, long y){return quotient(x,(Object)y);}
static public double quotient(double x, long y){return quotient(x,(double)y);}
static public double quotient(long x, double y){return quotient((double)x,y);}
static public Number remainder(double x, Object y){return remainder((Object)x,y);}
static public Number remainder(Object x, double y){return remainder(x,(Object)y);}
static public Number remainder(long x, Object y){return remainder((Object)x,y);}
static public Number remainder(Object x, long y){return remainder(x,(Object)y);}
static public double remainder(double x, long y){return remainder(x,(double)y);}
static public double remainder(long x, double y){return remainder((double)x,y);}
static public long add(long x, long y){
long ret = x + y;
if ((ret ^ x) < 0 && (ret ^ y) < 0)
return throwIntOverflow();
return ret;
}
static public Number addP(long x, long y){
long ret = x + y;
if ((ret ^ x) < 0 && (ret ^ y) < 0)
return addP((Number)x,(Number)y);
return num(ret);
}
static public long minus(long x, long y){
long ret = x - y;
if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
return throwIntOverflow();
return ret;
}
static public Number minusP(long x, long y){
long ret = x - y;
if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
return minusP((Number)x,(Number)y);
return num(ret);
}
static public long minus(long x){
if(x == Long.MIN_VALUE)
return throwIntOverflow();
return -x;
}
static public Number minusP(long x){
if(x == Long.MIN_VALUE)
return BigInt.fromBigInteger(BigInteger.valueOf(x).negate());
return num(-x);
}
static public long inc(long x){
if(x == Long.MAX_VALUE)
return throwIntOverflow();
return x + 1;
}
static public Number incP(long x){
if(x == Long.MAX_VALUE)
return BIGINT_OPS.inc(x);
return num(x + 1);
}
static public long dec(long x){
if(x == Long.MIN_VALUE)
return throwIntOverflow();
return x - 1;
}
static public Number decP(long x){
if(x == Long.MIN_VALUE)
return BIGINT_OPS.dec(x);
return num(x - 1);
}
static public long multiply(long x, long y){
if (x == Long.MIN_VALUE && y < 0)
return throwIntOverflow();
long ret = x * y;
if (y != 0 && ret/y != x)
return throwIntOverflow();
return ret;
}
static public Number multiplyP(long x, long y){
if (x == Long.MIN_VALUE && y < 0)
return multiplyP((Number)x,(Number)y);
long ret = x * y;
if (y != 0 && ret/y != x)
return multiplyP((Number)x,(Number)y);
return num(ret);
}
static public long quotient(long x, long y){
return x / y;
}
static public long remainder(long x, long y){
return x % y;
}
static public boolean equiv(long x, long y){
return x == y;
}
static public boolean lt(long x, long y){
return x < y;
}
static public boolean lte(long x, long y){
return x <= y;
}
static public boolean gt(long x, long y){
return x > y;
}
static public boolean gte(long x, long y){
return x >= y;
}
static public boolean isPos(long x){
return x > 0;
}
static public boolean isNeg(long x){
return x < 0;
}
static public boolean isZero(long x){
return x == 0;
}
/*
static public class F{
static public float add(float x, float y){
return x + y;
}
static public float subtract(float x, float y){
return x - y;
}
static public float negate(float x){
return -x;
}
static public float inc(float x){
return x + 1;
}
static public float dec(float x){
return x - 1;
}
static public float multiply(float x, float y){
return x * y;
}
static public float divide(float x, float y){
return x / y;
}
static public boolean equiv(float x, float y){
return x == y;
}
static public boolean lt(float x, float y){
return x < y;
}
static public boolean lte(float x, float y){
return x <= y;
}
static public boolean gt(float x, float y){
return x > y;
}
static public boolean gte(float x, float y){
return x >= y;
}
static public boolean pos(float x){
return x > 0;
}
static public boolean neg(float x){
return x < 0;
}
static public boolean zero(float x){
return x == 0;
}
static public float aget(float[] xs, int i){
return xs[i];
}
static public float aset(float[] xs, int i, float v){
xs[i] = v;
return v;
}
static public int alength(float[] xs){
return xs.length;
}
static public float[] aclone(float[] xs){
return xs.clone();
}
static public float[] vec(int size, Object init){
float[] ret = new float[size];
if(init instanceof Number)
{
float f = ((Number) init).floatValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).floatValue();
}
return ret;
}
static public float[] vec(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new float[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = s.count();
float[] ret = new float[size];
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).intValue();
return ret;
}
}
static public float[] vsadd(float[] x, float y){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += y;
return xs;
}
static public float[] vssub(float[] x, float y){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= y;
return xs;
}
static public float[] vsdiv(float[] x, float y){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= y;
return xs;
}
static public float[] vsmul(float[] x, float y){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= y;
return xs;
}
static public float[] svdiv(float y, float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = y / xs[i];
return xs;
}
static public float[] vsmuladd(float[] x, float y, float[] zs){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + zs[i];
return xs;
}
static public float[] vsmulsub(float[] x, float y, float[] zs){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - zs[i];
return xs;
}
static public float[] vsmulsadd(float[] x, float y, float z){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + z;
return xs;
}
static public float[] vsmulssub(float[] x, float y, float z){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - z;
return xs;
}
static public float[] vabs(float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.abs(xs[i]);
return xs;
}
static public float[] vnegabs(float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -Math.abs(xs[i]);
return xs;
}
static public float[] vneg(float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -xs[i];
return xs;
}
static public float[] vsqr(float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= xs[i];
return xs;
}
static public float[] vsignedsqr(float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= Math.abs(xs[i]);
return xs;
}
static public float[] vclip(float[] x, float low, float high){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
xs[i] = low;
else if(xs[i] > high)
xs[i] = high;
}
return xs;
}
static public IPersistentVector vclipcounts(float[] x, float low, float high){
final float[] xs = x.clone();
int lowc = 0;
int highc = 0;
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
{
++lowc;
xs[i] = low;
}
else if(xs[i] > high)
{
++highc;
xs[i] = high;
}
}
return RT.vector(xs, lowc, highc);
}
static public float[] vthresh(float[] x, float thresh, float otherwise){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < thresh)
xs[i] = otherwise;
}
return xs;
}
static public float[] vreverse(float[] x){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[xs.length - i - 1];
return xs;
}
static public float[] vrunningsum(float[] x){
final float[] xs = x.clone();
for(int i = 1; i < xs.length; i++)
xs[i] = xs[i - 1] + xs[i];
return xs;
}
static public float[] vsort(float[] x){
final float[] xs = x.clone();
Arrays.sort(xs);
return xs;
}
static public float vdot(float[] xs, float[] ys){
float ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * ys[i];
return ret;
}
static public float vmax(float[] xs){
if(xs.length == 0)
return 0;
float ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.max(ret, xs[i]);
return ret;
}
static public float vmin(float[] xs){
if(xs.length == 0)
return 0;
float ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.min(ret, xs[i]);
return ret;
}
static public float vmean(float[] xs){
if(xs.length == 0)
return 0;
return vsum(xs) / xs.length;
}
static public double vrms(float[] xs){
if(xs.length == 0)
return 0;
float ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * xs[i];
return Math.sqrt(ret / xs.length);
}
static public float vsum(float[] xs){
float ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i];
return ret;
}
static public boolean vequiv(float[] xs, float[] ys){
return Arrays.equals(xs, ys);
}
static public float[] vadd(float[] x, float[] ys){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += ys[i];
return xs;
}
static public float[] vsub(float[] x, float[] ys){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= ys[i];
return xs;
}
static public float[] vaddmul(float[] x, float[] ys, float[] zs){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * zs[i];
return xs;
}
static public float[] vsubmul(float[] x, float[] ys, float[] zs){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * zs[i];
return xs;
}
static public float[] vaddsmul(float[] x, float[] ys, float z){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * z;
return xs;
}
static public float[] vsubsmul(float[] x, float[] ys, float z){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * z;
return xs;
}
static public float[] vmulsadd(float[] x, float[] ys, float z){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + z;
return xs;
}
static public float[] vdiv(float[] x, float[] ys){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= ys[i];
return xs;
}
static public float[] vmul(float[] x, float[] ys){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= ys[i];
return xs;
}
static public float[] vmuladd(float[] x, float[] ys, float[] zs){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + zs[i];
return xs;
}
static public float[] vmulsub(float[] x, float[] ys, float[] zs){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) - zs[i];
return xs;
}
static public float[] vmax(float[] x, float[] ys){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.max(xs[i], ys[i]);
return xs;
}
static public float[] vmin(float[] x, float[] ys){
final float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.min(xs[i], ys[i]);
return xs;
}
static public float[] vmap(IFn fn, float[] x) {
float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i])).floatValue();
return xs;
}
static public float[] vmap(IFn fn, float[] x, float[] ys) {
float[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i], ys[i])).floatValue();
return xs;
}
}
static public class D{
static public double add(double x, double y){
return x + y;
}
static public double subtract(double x, double y){
return x - y;
}
static public double negate(double x){
return -x;
}
static public double inc(double x){
return x + 1;
}
static public double dec(double x){
return x - 1;
}
static public double multiply(double x, double y){
return x * y;
}
static public double divide(double x, double y){
return x / y;
}
static public boolean equiv(double x, double y){
return x == y;
}
static public boolean lt(double x, double y){
return x < y;
}
static public boolean lte(double x, double y){
return x <= y;
}
static public boolean gt(double x, double y){
return x > y;
}
static public boolean gte(double x, double y){
return x >= y;
}
static public boolean pos(double x){
return x > 0;
}
static public boolean neg(double x){
return x < 0;
}
static public boolean zero(double x){
return x == 0;
}
static public double aget(double[] xs, int i){
return xs[i];
}
static public double aset(double[] xs, int i, double v){
xs[i] = v;
return v;
}
static public int alength(double[] xs){
return xs.length;
}
static public double[] aclone(double[] xs){
return xs.clone();
}
static public double[] vec(int size, Object init){
double[] ret = new double[size];
if(init instanceof Number)
{
double f = ((Number) init).doubleValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).doubleValue();
}
return ret;
}
static public double[] vec(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new double[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = s.count();
double[] ret = new double[size];
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).intValue();
return ret;
}
}
static public double[] vsadd(double[] x, double y){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += y;
return xs;
}
static public double[] vssub(double[] x, double y){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= y;
return xs;
}
static public double[] vsdiv(double[] x, double y){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= y;
return xs;
}
static public double[] vsmul(double[] x, double y){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= y;
return xs;
}
static public double[] svdiv(double y, double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = y / xs[i];
return xs;
}
static public double[] vsmuladd(double[] x, double y, double[] zs){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + zs[i];
return xs;
}
static public double[] vsmulsub(double[] x, double y, double[] zs){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - zs[i];
return xs;
}
static public double[] vsmulsadd(double[] x, double y, double z){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + z;
return xs;
}
static public double[] vsmulssub(double[] x, double y, double z){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - z;
return xs;
}
static public double[] vabs(double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.abs(xs[i]);
return xs;
}
static public double[] vnegabs(double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -Math.abs(xs[i]);
return xs;
}
static public double[] vneg(double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -xs[i];
return xs;
}
static public double[] vsqr(double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= xs[i];
return xs;
}
static public double[] vsignedsqr(double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= Math.abs(xs[i]);
return xs;
}
static public double[] vclip(double[] x, double low, double high){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
xs[i] = low;
else if(xs[i] > high)
xs[i] = high;
}
return xs;
}
static public IPersistentVector vclipcounts(double[] x, double low, double high){
final double[] xs = x.clone();
int lowc = 0;
int highc = 0;
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
{
++lowc;
xs[i] = low;
}
else if(xs[i] > high)
{
++highc;
xs[i] = high;
}
}
return RT.vector(xs, lowc, highc);
}
static public double[] vthresh(double[] x, double thresh, double otherwise){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < thresh)
xs[i] = otherwise;
}
return xs;
}
static public double[] vreverse(double[] x){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[xs.length - i - 1];
return xs;
}
static public double[] vrunningsum(double[] x){
final double[] xs = x.clone();
for(int i = 1; i < xs.length; i++)
xs[i] = xs[i - 1] + xs[i];
return xs;
}
static public double[] vsort(double[] x){
final double[] xs = x.clone();
Arrays.sort(xs);
return xs;
}
static public double vdot(double[] xs, double[] ys){
double ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * ys[i];
return ret;
}
static public double vmax(double[] xs){
if(xs.length == 0)
return 0;
double ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.max(ret, xs[i]);
return ret;
}
static public double vmin(double[] xs){
if(xs.length == 0)
return 0;
double ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.min(ret, xs[i]);
return ret;
}
static public double vmean(double[] xs){
if(xs.length == 0)
return 0;
return vsum(xs) / xs.length;
}
static public double vrms(double[] xs){
if(xs.length == 0)
return 0;
double ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * xs[i];
return Math.sqrt(ret / xs.length);
}
static public double vsum(double[] xs){
double ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i];
return ret;
}
static public boolean vequiv(double[] xs, double[] ys){
return Arrays.equals(xs, ys);
}
static public double[] vadd(double[] x, double[] ys){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += ys[i];
return xs;
}
static public double[] vsub(double[] x, double[] ys){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= ys[i];
return xs;
}
static public double[] vaddmul(double[] x, double[] ys, double[] zs){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * zs[i];
return xs;
}
static public double[] vsubmul(double[] x, double[] ys, double[] zs){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * zs[i];
return xs;
}
static public double[] vaddsmul(double[] x, double[] ys, double z){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * z;
return xs;
}
static public double[] vsubsmul(double[] x, double[] ys, double z){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * z;
return xs;
}
static public double[] vmulsadd(double[] x, double[] ys, double z){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + z;
return xs;
}
static public double[] vdiv(double[] x, double[] ys){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= ys[i];
return xs;
}
static public double[] vmul(double[] x, double[] ys){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= ys[i];
return xs;
}
static public double[] vmuladd(double[] x, double[] ys, double[] zs){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + zs[i];
return xs;
}
static public double[] vmulsub(double[] x, double[] ys, double[] zs){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) - zs[i];
return xs;
}
static public double[] vmax(double[] x, double[] ys){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.max(xs[i], ys[i]);
return xs;
}
static public double[] vmin(double[] x, double[] ys){
final double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.min(xs[i], ys[i]);
return xs;
}
static public double[] vmap(IFn fn, double[] x) {
double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i])).doubleValue();
return xs;
}
static public double[] vmap(IFn fn, double[] x, double[] ys) {
double[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i], ys[i])).doubleValue();
return xs;
}
}
static public class I{
static public int add(int x, int y){
return x + y;
}
static public int subtract(int x, int y){
return x - y;
}
static public int negate(int x){
return -x;
}
static public int inc(int x){
return x + 1;
}
static public int dec(int x){
return x - 1;
}
static public int multiply(int x, int y){
return x * y;
}
static public int divide(int x, int y){
return x / y;
}
static public boolean equiv(int x, int y){
return x == y;
}
static public boolean lt(int x, int y){
return x < y;
}
static public boolean lte(int x, int y){
return x <= y;
}
static public boolean gt(int x, int y){
return x > y;
}
static public boolean gte(int x, int y){
return x >= y;
}
static public boolean pos(int x){
return x > 0;
}
static public boolean neg(int x){
return x < 0;
}
static public boolean zero(int x){
return x == 0;
}
static public int aget(int[] xs, int i){
return xs[i];
}
static public int aset(int[] xs, int i, int v){
xs[i] = v;
return v;
}
static public int alength(int[] xs){
return xs.length;
}
static public int[] aclone(int[] xs){
return xs.clone();
}
static public int[] vec(int size, Object init){
int[] ret = new int[size];
if(init instanceof Number)
{
int f = ((Number) init).intValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).intValue();
}
return ret;
}
static public int[] vec(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new int[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = s.count();
int[] ret = new int[size];
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).intValue();
return ret;
}
}
static public int[] vsadd(int[] x, int y){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += y;
return xs;
}
static public int[] vssub(int[] x, int y){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= y;
return xs;
}
static public int[] vsdiv(int[] x, int y){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= y;
return xs;
}
static public int[] vsmul(int[] x, int y){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= y;
return xs;
}
static public int[] svdiv(int y, int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = y / xs[i];
return xs;
}
static public int[] vsmuladd(int[] x, int y, int[] zs){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + zs[i];
return xs;
}
static public int[] vsmulsub(int[] x, int y, int[] zs){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - zs[i];
return xs;
}
static public int[] vsmulsadd(int[] x, int y, int z){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + z;
return xs;
}
static public int[] vsmulssub(int[] x, int y, int z){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - z;
return xs;
}
static public int[] vabs(int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.abs(xs[i]);
return xs;
}
static public int[] vnegabs(int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -Math.abs(xs[i]);
return xs;
}
static public int[] vneg(int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -xs[i];
return xs;
}
static public int[] vsqr(int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= xs[i];
return xs;
}
static public int[] vsignedsqr(int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= Math.abs(xs[i]);
return xs;
}
static public int[] vclip(int[] x, int low, int high){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
xs[i] = low;
else if(xs[i] > high)
xs[i] = high;
}
return xs;
}
static public IPersistentVector vclipcounts(int[] x, int low, int high){
final int[] xs = x.clone();
int lowc = 0;
int highc = 0;
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
{
++lowc;
xs[i] = low;
}
else if(xs[i] > high)
{
++highc;
xs[i] = high;
}
}
return RT.vector(xs, lowc, highc);
}
static public int[] vthresh(int[] x, int thresh, int otherwise){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < thresh)
xs[i] = otherwise;
}
return xs;
}
static public int[] vreverse(int[] x){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[xs.length - i - 1];
return xs;
}
static public int[] vrunningsum(int[] x){
final int[] xs = x.clone();
for(int i = 1; i < xs.length; i++)
xs[i] = xs[i - 1] + xs[i];
return xs;
}
static public int[] vsort(int[] x){
final int[] xs = x.clone();
Arrays.sort(xs);
return xs;
}
static public int vdot(int[] xs, int[] ys){
int ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * ys[i];
return ret;
}
static public int vmax(int[] xs){
if(xs.length == 0)
return 0;
int ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.max(ret, xs[i]);
return ret;
}
static public int vmin(int[] xs){
if(xs.length == 0)
return 0;
int ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.min(ret, xs[i]);
return ret;
}
static public double vmean(int[] xs){
if(xs.length == 0)
return 0;
return vsum(xs) / (double) xs.length;
}
static public double vrms(int[] xs){
if(xs.length == 0)
return 0;
int ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * xs[i];
return Math.sqrt(ret / (double) xs.length);
}
static public int vsum(int[] xs){
int ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i];
return ret;
}
static public boolean vequiv(int[] xs, int[] ys){
return Arrays.equals(xs, ys);
}
static public int[] vadd(int[] x, int[] ys){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += ys[i];
return xs;
}
static public int[] vsub(int[] x, int[] ys){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= ys[i];
return xs;
}
static public int[] vaddmul(int[] x, int[] ys, int[] zs){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * zs[i];
return xs;
}
static public int[] vsubmul(int[] x, int[] ys, int[] zs){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * zs[i];
return xs;
}
static public int[] vaddsmul(int[] x, int[] ys, int z){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * z;
return xs;
}
static public int[] vsubsmul(int[] x, int[] ys, int z){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * z;
return xs;
}
static public int[] vmulsadd(int[] x, int[] ys, int z){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + z;
return xs;
}
static public int[] vdiv(int[] x, int[] ys){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= ys[i];
return xs;
}
static public int[] vmul(int[] x, int[] ys){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= ys[i];
return xs;
}
static public int[] vmuladd(int[] x, int[] ys, int[] zs){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + zs[i];
return xs;
}
static public int[] vmulsub(int[] x, int[] ys, int[] zs){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) - zs[i];
return xs;
}
static public int[] vmax(int[] x, int[] ys){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.max(xs[i], ys[i]);
return xs;
}
static public int[] vmin(int[] x, int[] ys){
final int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.min(xs[i], ys[i]);
return xs;
}
static public int[] vmap(IFn fn, int[] x) {
int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i])).intValue();
return xs;
}
static public int[] vmap(IFn fn, int[] x, int[] ys) {
int[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i], ys[i])).intValue();
return xs;
}
}
static public class L{
static public long add(long x, long y){
return x + y;
}
static public long subtract(long x, long y){
return x - y;
}
static public long negate(long x){
return -x;
}
static public long inc(long x){
return x + 1;
}
static public long dec(long x){
return x - 1;
}
static public long multiply(long x, long y){
return x * y;
}
static public long divide(long x, long y){
return x / y;
}
static public boolean equiv(long x, long y){
return x == y;
}
static public boolean lt(long x, long y){
return x < y;
}
static public boolean lte(long x, long y){
return x <= y;
}
static public boolean gt(long x, long y){
return x > y;
}
static public boolean gte(long x, long y){
return x >= y;
}
static public boolean pos(long x){
return x > 0;
}
static public boolean neg(long x){
return x < 0;
}
static public boolean zero(long x){
return x == 0;
}
static public long aget(long[] xs, int i){
return xs[i];
}
static public long aset(long[] xs, int i, long v){
xs[i] = v;
return v;
}
static public int alength(long[] xs){
return xs.length;
}
static public long[] aclone(long[] xs){
return xs.clone();
}
static public long[] vec(int size, Object init){
long[] ret = new long[size];
if(init instanceof Number)
{
long f = ((Number) init).longValue();
for(int i = 0; i < ret.length; i++)
ret[i] = f;
}
else
{
ISeq s = RT.seq(init);
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).longValue();
}
return ret;
}
static public long[] vec(Object sizeOrSeq){
if(sizeOrSeq instanceof Number)
return new long[((Number) sizeOrSeq).intValue()];
else
{
ISeq s = RT.seq(sizeOrSeq);
int size = s.count();
long[] ret = new long[size];
for(int i = 0; i < size && s != null; i++, s = s.rest())
ret[i] = ((Number) s.first()).intValue();
return ret;
}
}
static public long[] vsadd(long[] x, long y){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += y;
return xs;
}
static public long[] vssub(long[] x, long y){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= y;
return xs;
}
static public long[] vsdiv(long[] x, long y){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= y;
return xs;
}
static public long[] vsmul(long[] x, long y){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= y;
return xs;
}
static public long[] svdiv(long y, long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = y / xs[i];
return xs;
}
static public long[] vsmuladd(long[] x, long y, long[] zs){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + zs[i];
return xs;
}
static public long[] vsmulsub(long[] x, long y, long[] zs){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - zs[i];
return xs;
}
static public long[] vsmulsadd(long[] x, long y, long z){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y + z;
return xs;
}
static public long[] vsmulssub(long[] x, long y, long z){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[i] * y - z;
return xs;
}
static public long[] vabs(long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.abs(xs[i]);
return xs;
}
static public long[] vnegabs(long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -Math.abs(xs[i]);
return xs;
}
static public long[] vneg(long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = -xs[i];
return xs;
}
static public long[] vsqr(long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= xs[i];
return xs;
}
static public long[] vsignedsqr(long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= Math.abs(xs[i]);
return xs;
}
static public long[] vclip(long[] x, long low, long high){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
xs[i] = low;
else if(xs[i] > high)
xs[i] = high;
}
return xs;
}
static public IPersistentVector vclipcounts(long[] x, long low, long high){
final long[] xs = x.clone();
int lowc = 0;
int highc = 0;
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < low)
{
++lowc;
xs[i] = low;
}
else if(xs[i] > high)
{
++highc;
xs[i] = high;
}
}
return RT.vector(xs, lowc, highc);
}
static public long[] vthresh(long[] x, long thresh, long otherwise){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
{
if(xs[i] < thresh)
xs[i] = otherwise;
}
return xs;
}
static public long[] vreverse(long[] x){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = xs[xs.length - i - 1];
return xs;
}
static public long[] vrunningsum(long[] x){
final long[] xs = x.clone();
for(int i = 1; i < xs.length; i++)
xs[i] = xs[i - 1] + xs[i];
return xs;
}
static public long[] vsort(long[] x){
final long[] xs = x.clone();
Arrays.sort(xs);
return xs;
}
static public long vdot(long[] xs, long[] ys){
long ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * ys[i];
return ret;
}
static public long vmax(long[] xs){
if(xs.length == 0)
return 0;
long ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.max(ret, xs[i]);
return ret;
}
static public long vmin(long[] xs){
if(xs.length == 0)
return 0;
long ret = xs[0];
for(int i = 0; i < xs.length; i++)
ret = Math.min(ret, xs[i]);
return ret;
}
static public double vmean(long[] xs){
if(xs.length == 0)
return 0;
return vsum(xs) / (double) xs.length;
}
static public double vrms(long[] xs){
if(xs.length == 0)
return 0;
long ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i] * xs[i];
return Math.sqrt(ret / (double) xs.length);
}
static public long vsum(long[] xs){
long ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i];
return ret;
}
static public boolean vequiv(long[] xs, long[] ys){
return Arrays.equals(xs, ys);
}
static public long[] vadd(long[] x, long[] ys){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] += ys[i];
return xs;
}
static public long[] vsub(long[] x, long[] ys){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] -= ys[i];
return xs;
}
static public long[] vaddmul(long[] x, long[] ys, long[] zs){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * zs[i];
return xs;
}
static public long[] vsubmul(long[] x, long[] ys, long[] zs){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * zs[i];
return xs;
}
static public long[] vaddsmul(long[] x, long[] ys, long z){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] + ys[i]) * z;
return xs;
}
static public long[] vsubsmul(long[] x, long[] ys, long z){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] - ys[i]) * z;
return xs;
}
static public long[] vmulsadd(long[] x, long[] ys, long z){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + z;
return xs;
}
static public long[] vdiv(long[] x, long[] ys){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] /= ys[i];
return xs;
}
static public long[] vmul(long[] x, long[] ys){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] *= ys[i];
return xs;
}
static public long[] vmuladd(long[] x, long[] ys, long[] zs){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) + zs[i];
return xs;
}
static public long[] vmulsub(long[] x, long[] ys, long[] zs){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = (xs[i] * ys[i]) - zs[i];
return xs;
}
static public long[] vmax(long[] x, long[] ys){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.max(xs[i], ys[i]);
return xs;
}
static public long[] vmin(long[] x, long[] ys){
final long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = Math.min(xs[i], ys[i]);
return xs;
}
static public long[] vmap(IFn fn, long[] x) {
long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i])).longValue();
return xs;
}
static public long[] vmap(IFn fn, long[] x, long[] ys) {
long[] xs = x.clone();
for(int i = 0; i < xs.length; i++)
xs[i] = ((Number) fn.invoke(xs[i], ys[i])).longValue();
return xs;
}
}
*/
//overload resolution
//*
static public Number add(long x, Object y){
return add((Object)x,y);
}
static public Number add(Object x, long y){
return add(x,(Object)y);
}
static public Number addP(long x, Object y){
return addP((Object)x,y);
}
static public Number addP(Object x, long y){
return addP(x,(Object)y);
}
static public double add(double x, Object y){
return add(x,((Number)y).doubleValue());
}
static public double add(Object x, double y){
return add(((Number)x).doubleValue(),y);
}
static public double add(double x, long y){
return x + y;
}
static public double add(long x, double y){
return x + y;
}
static public double addP(double x, Object y){
return addP(x,((Number)y).doubleValue());
}
static public double addP(Object x, double y){
return addP(((Number)x).doubleValue(),y);
}
static public double addP(double x, long y){
return x + y;
}
static public double addP(long x, double y){
return x + y;
}
static public Number minus(long x, Object y){
return minus((Object)x,y);
}
static public Number minus(Object x, long y){
return minus(x,(Object)y);
}
static public Number minusP(long x, Object y){
return minusP((Object)x,y);
}
static public Number minusP(Object x, long y){
return minusP(x,(Object)y);
}
static public double minus(double x, Object y){
return minus(x,((Number)y).doubleValue());
}
static public double minus(Object x, double y){
return minus(((Number)x).doubleValue(),y);
}
static public double minus(double x, long y){
return x - y;
}
static public double minus(long x, double y){
return x - y;
}
static public double minusP(double x, Object y){
return minus(x,((Number)y).doubleValue());
}
static public double minusP(Object x, double y){
return minus(((Number)x).doubleValue(),y);
}
static public double minusP(double x, long y){
return x - y;
}
static public double minusP(long x, double y){
return x - y;
}
static public Number multiply(long x, Object y){
return multiply((Object)x,y);
}
static public Number multiply(Object x, long y){
return multiply(x,(Object)y);
}
static public Number multiplyP(long x, Object y){
return multiplyP((Object)x,y);
}
static public Number multiplyP(Object x, long y){
return multiplyP(x,(Object)y);
}
static public double multiply(double x, Object y){
return multiply(x,((Number)y).doubleValue());
}
static public double multiply(Object x, double y){
return multiply(((Number)x).doubleValue(),y);
}
static public double multiply(double x, long y){
return x * y;
}
static public double multiply(long x, double y){
return x * y;
}
static public double multiplyP(double x, Object y){
return multiplyP(x,((Number)y).doubleValue());
}
static public double multiplyP(Object x, double y){
return multiplyP(((Number)x).doubleValue(),y);
}
static public double multiplyP(double x, long y){
return x * y;
}
static public double multiplyP(long x, double y){
return x * y;
}
static public Number divide(long x, Object y){
return divide((Object)x,y);
}
static public Number divide(Object x, long y){
return divide(x,(Object)y);
}
static public double divide(double x, Object y){
return x / ((Number)y).doubleValue();
}
static public double divide(Object x, double y){
return ((Number)x).doubleValue() / y;
}
static public double divide(double x, long y){
return x / y;
}
static public double divide(long x, double y){
return x / y;
}
static public Number divide(long x, long y){
return divide((Number)x, (Number)y);
}
static public boolean lt(long x, Object y){
return lt((Object)x,y);
}
static public boolean lt(Object x, long y){
return lt(x,(Object)y);
}
static public boolean lt(double x, Object y){
return x < ((Number)y).doubleValue();
}
static public boolean lt(Object x, double y){
return ((Number)x).doubleValue() < y;
}
static public boolean lt(double x, long y){
return x < y;
}
static public boolean lt(long x, double y){
return x < y;
}
static public boolean lte(long x, Object y){
return lte((Object)x,y);
}
static public boolean lte(Object x, long y){
return lte(x,(Object)y);
}
static public boolean lte(double x, Object y){
return x <= ((Number)y).doubleValue();
}
static public boolean lte(Object x, double y){
return ((Number)x).doubleValue() <= y;
}
static public boolean lte(double x, long y){
return x <= y;
}
static public boolean lte(long x, double y){
return x <= y;
}
static public boolean gt(long x, Object y){
return gt((Object)x,y);
}
static public boolean gt(Object x, long y){
return gt(x,(Object)y);
}
static public boolean gt(double x, Object y){
return x > ((Number)y).doubleValue();
}
static public boolean gt(Object x, double y){
return ((Number)x).doubleValue() > y;
}
static public boolean gt(double x, long y){
return x > y;
}
static public boolean gt(long x, double y){
return x > y;
}
static public boolean gte(long x, Object y){
return gte((Object)x,y);
}
static public boolean gte(Object x, long y){
return gte(x,(Object)y);
}
static public boolean gte(double x, Object y){
return x >= ((Number)y).doubleValue();
}
static public boolean gte(Object x, double y){
return ((Number)x).doubleValue() >= y;
}
static public boolean gte(double x, long y){
return x >= y;
}
static public boolean gte(long x, double y){
return x >= y;
}
static public boolean equiv(long x, Object y){
return equiv((Object)x,y);
}
static public boolean equiv(Object x, long y){
return equiv(x,(Object)y);
}
static public boolean equiv(double x, Object y){
return x == ((Number)y).doubleValue();
}
static public boolean equiv(Object x, double y){
return ((Number)x).doubleValue() == y;
}
static public boolean equiv(double x, long y){
return x == y;
}
static public boolean equiv(long x, double y){
return x == y;
}
static boolean isNaN(Object x){
return (x instanceof Double) && ((Double)x).isNaN()
|| (x instanceof Float) && ((Float)x).isNaN();
}
static public double max(double x, double y){
return Math.max(x, y);
}
static public Object max(double x, long y){
if(Double.isNaN(x)){
return x;
}
if(x > y){
return x;
} else {
return y;
}
}
static public Object max(double x, Object y){
if(Double.isNaN(x)){
return x;
} else if(isNaN(y)){
return y;
}
if(x > ((Number)y).doubleValue()){
return x;
} else {
return y;
}
}
static public Object max(long x, double y){
if(Double.isNaN(y)){
return y;
}
if(x > y){
return x;
} else {
return y;
}
}
static public long max(long x, long y){
if(x > y) {
return x;
} else {
return y;
}
}
static public Object max(long x, Object y){
if(isNaN(y)){
return y;
}
if(gt(x,y)){
return x;
} else {
return y;
}
}
static public Object max(Object x, long y){
if(isNaN(x)){
return x;
}
if(gt(x,y)){
return x;
} else {
return y;
}
}
static public Object max(Object x, double y){
if (isNaN(x)){
return x;
} else if(Double.isNaN(y)){
return y;
}
if(((Number)x).doubleValue() > y){
return x;
} else {
return y;
}
}
static public Object max(Object x, Object y){
if(isNaN(x)){
return x;
} else if(isNaN(y)){
return y;
}
if(gt(x, y)) {
return x;
} else {
return y;
}
}
static public double min(double x, double y){
return Math.min(x, y);
}
static public Object min(double x, long y){
if (Double.isNaN(x)){
return x;
}
if(x < y){
return x;
} else {
return y;
}
}
static public Object min(double x, Object y){
if(Double.isNaN(x)){
return x;
} else if(isNaN(y)){
return y;
}
if(x < ((Number)y).doubleValue()){
return x;
} else {
return y;
}
}
static public Object min(long x, double y){
if(Double.isNaN(y)){
return y;
}
if(x < y){
return x;
} else {
return y;
}
}
static public long min(long x, long y){
if(x < y) {
return x;
} else {
return y;
}
}
static public Object min(long x, Object y){
if(isNaN(y)){
return y;
}
if(lt(x,y)){
return x;
} else {
return y;
}
}
static public Object min(Object x, long y){
if(isNaN(x)){
return x;
}
if(lt(x,y)){
return x;
} else {
return y;
}
}
static public Object min(Object x, double y){
if(isNaN(x)){
return x;
} else if(Double.isNaN(y)){
return y;
}
if(((Number)x).doubleValue() < y){
return x;
} else {
return y;
}
}
static public Object min(Object x, Object y){
if (isNaN(x)){
return x;
} else if(isNaN(y)){
return y;
}
if(lt(x,y)) {
return x;
} else {
return y;
}
}
}
================================================
FILE: src/jvm/clojure/lang/Obj.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 3:44:58 PM */
package clojure.lang;
import java.io.Serializable;
public abstract class Obj implements IObj, Serializable {
final IPersistentMap _meta;
public Obj(IPersistentMap meta){
this._meta = meta;
}
public Obj(){
_meta = null;
}
final public IPersistentMap meta(){
return _meta;
}
abstract public Obj withMeta(IPersistentMap meta);
}
================================================
FILE: src/jvm/clojure/lang/ObjC.java
================================================
package clojure.lang;
public class ObjC {
public static boolean objc = false;
public static void setObjC() {
objc = true;
}
}
================================================
FILE: src/jvm/clojure/lang/ObjCClass.java
================================================
package clojure.lang;
public class ObjCClass {
private final String name;
public ObjCClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
================================================
FILE: src/jvm/clojure/lang/PersistentArrayMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
/**
* Simple implementation of persistent map on an array
*
* Note that instances of this class are constant values
* i.e. add/remove etc return new values
*
* Copies array on every change, so only appropriate for _very_small_ maps
*
* null keys and values are ok, but you won't be able to distinguish a null value via valAt - use contains/entryAt
*/
public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable {
final Object[] array;
static final int HASHTABLE_THRESHOLD = 16;
public static final PersistentArrayMap EMPTY = new PersistentArrayMap();
private final IPersistentMap _meta;
static public IPersistentMap create(Map other){
ITransientMap ret = EMPTY.asTransient();
for(Object o : other.entrySet())
{
Map.Entry e = (Entry) o;
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret.persistent();
}
protected PersistentArrayMap(){
this.array = new Object[]{};
this._meta = null;
}
public PersistentArrayMap withMeta(IPersistentMap meta){
return new PersistentArrayMap(meta, array);
}
PersistentArrayMap create(Object... init){
return new PersistentArrayMap(meta(), init);
}
IPersistentMap createHT(Object[] init){
return PersistentHashMap.create(meta(), init);
}
static public PersistentArrayMap createWithCheck(Object[] init){
for(int i=0;i< init.length;i += 2)
{
for(int j=i+2;j=i; j -= 2)
{
if(equalKey(init[i],init[j]))
{
break;
}
}
nodups[m] = init[i];
nodups[m+1] = init[j+1];
m += 2;
}
}
if (m != n)
throw new IllegalArgumentException("Internal error: m=" + m);
init = nodups;
}
return new PersistentArrayMap(init);
}
/**
* This ctor captures/aliases the passed array, so do not modify later
*
* @param init {key1,val1,key2,val2,...}
*/
public PersistentArrayMap(Object[] init){
this.array = init;
this._meta = null;
}
public PersistentArrayMap(IPersistentMap meta, Object[] init){
this._meta = meta;
this.array = init;
}
public int count(){
return array.length / 2;
}
public boolean containsKey(Object key){
return indexOf(key) >= 0;
}
public IMapEntry entryAt(Object key){
int i = indexOf(key);
if(i >= 0)
return new MapEntry(array[i],array[i+1]);
return null;
}
public IPersistentMap assocEx(Object key, Object val) {
int i = indexOf(key);
Object[] newArray;
if(i >= 0)
{
throw Util.runtimeException("Key already present");
}
else //didn't have key, grow
{
if(array.length > HASHTABLE_THRESHOLD)
return createHT(array).assocEx(key, val);
newArray = new Object[array.length + 2];
if(array.length > 0)
System.arraycopy(array, 0, newArray, 2, array.length);
newArray[0] = key;
newArray[1] = val;
}
return create(newArray);
}
public IPersistentMap assoc(Object key, Object val){
int i = indexOf(key);
Object[] newArray;
if(i >= 0) //already have key, same-sized replacement
{
if(array[i + 1] == val) //no change, no op
return this;
newArray = array.clone();
newArray[i + 1] = val;
}
else //didn't have key, grow
{
if(array.length > HASHTABLE_THRESHOLD)
return createHT(array).assoc(key, val);
newArray = new Object[array.length + 2];
if(array.length > 0)
System.arraycopy(array, 0, newArray, 0, array.length);
newArray[newArray.length-2] = key;
newArray[newArray.length-1] = val;
}
return create(newArray);
}
public IPersistentMap without(Object key){
int i = indexOf(key);
if(i >= 0) //have key, will remove
{
int newlen = array.length - 2;
if(newlen == 0)
return empty();
Object[] newArray = new Object[newlen];
for(int s = 0, d = 0; s < array.length; s += 2)
{
if(!equalKey(array[s], key)) //skip removal key
{
newArray[d] = array[s];
newArray[d + 1] = array[s + 1];
d += 2;
}
}
return create(newArray);
}
//don't have key, no op
return this;
}
public IPersistentMap empty(){
return (IPersistentMap) EMPTY.withMeta(meta());
}
final public Object valAt(Object key, Object notFound){
int i = indexOf(key);
if(i >= 0)
return array[i + 1];
return notFound;
}
public Object valAt(Object key){
return valAt(key, null);
}
public int capacity(){
return count();
}
private int indexOfObject(Object key){
Util.EquivPred ep = Util.equivPred(key);
for(int i = 0; i < array.length; i += 2)
{
if(ep.equiv(key, array[i]))
return i;
}
return -1;
}
private int indexOf(Object key){
if(key instanceof Keyword)
{
for(int i = 0; i < array.length; i += 2)
{
if(key == array[i])
return i;
}
return -1;
}
else
return indexOfObject(key);
}
static boolean equalKey(Object k1, Object k2){
if(k1 instanceof Keyword)
return k1 == k2;
return Util.equiv(k1, k2);
}
public Iterator iterator(){
return new Iter(array,APersistentMap.MAKE_ENTRY);
}
public Iterator keyIterator(){
return new Iter(array,APersistentMap.MAKE_KEY);
}
public Iterator valIterator() {
return new Iter(array,APersistentMap.MAKE_VAL);
}
public ISeq seq(){
if(array.length > 0)
return new Seq(array, 0);
return null;
}
public IPersistentMap meta(){
return _meta;
}
static class Seq extends ASeq implements Counted{
final Object[] array;
final int i;
Seq(Object[] array, int i){
this.array = array;
this.i = i;
}
public Seq(IPersistentMap meta, Object[] array, int i){
super(meta);
this.array = array;
this.i = i;
}
public Object first(){
return new MapEntry(array[i],array[i+1]);
}
public ISeq next(){
if(i + 2 < array.length)
return new Seq(array, i + 2);
return null;
}
public int count(){
return (array.length - i) / 2;
}
public Obj withMeta(IPersistentMap meta){
return new Seq(meta, array, i);
}
}
static class Iter implements Iterator{
IFn f;
Object[] array;
int i;
//for iterator
Iter(Object[] array, IFn f){
this(array, -2, f);
}
//for entryAt
Iter(Object[] array, int i, IFn f){
this.array = array;
this.i = i;
this.f = f;
}
public boolean hasNext(){
return i < array.length - 2;
}
public Object next(){
i += 2;
return f.invoke(array[i],array[i+1]);
}
public void remove(){
throw new UnsupportedOperationException();
}
}
public Object kvreduce(IFn f, Object init){
for(int i=0;i < array.length;i+=2){
init = f.invoke(init, array[i], array[i+1]);
if(RT.isReduced(init))
return ((IDeref)init).deref();
}
return init;
}
public ITransientMap asTransient(){
return new TransientArrayMap(array);
}
static final class TransientArrayMap extends ATransientMap {
volatile int len;
final Object[] array;
volatile Thread owner;
public TransientArrayMap(Object[] array){
this.owner = Thread.currentThread();
this.array = new Object[Math.max(HASHTABLE_THRESHOLD, array.length)];
System.arraycopy(array, 0, this.array, 0, array.length);
this.len = array.length;
}
private int indexOf(Object key){
for(int i = 0; i < len; i += 2)
{
if(equalKey(array[i], key))
return i;
}
return -1;
}
ITransientMap doAssoc(Object key, Object val){
int i = indexOf(key);
if(i >= 0) //already have key,
{
if(array[i + 1] != val) //no change, no op
array[i + 1] = val;
}
else //didn't have key, grow
{
if(len >= array.length)
return PersistentHashMap.create(array).asTransient().assoc(key, val);
array[len++] = key;
array[len++] = val;
}
return this;
}
ITransientMap doWithout(Object key) {
int i = indexOf(key);
if(i >= 0) //have key, will remove
{
if (len >= 2)
{
array[i] = array[len - 2];
array[i + 1] = array[len - 1];
}
len -= 2;
}
return this;
}
Object doValAt(Object key, Object notFound) {
int i = indexOf(key);
if (i >= 0)
return array[i + 1];
return notFound;
}
int doCount() {
return len / 2;
}
IPersistentMap doPersistent(){
ensureEditable();
owner = null;
Object[] a = new Object[len];
System.arraycopy(array,0,a,0,len);
return new PersistentArrayMap(a);
}
void ensureEditable(){
if(owner == null)
throw new IllegalAccessError("Transient used after persistent! call");
}
}
}
================================================
FILE: src/jvm/clojure/lang/PersistentHashMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
/*
A persistent rendition of Phil Bagwell's Hash Array Mapped Trie
Uses path copying for persistence
HashCollision leaves vs. extended hashing
Node polymorphism vs. conditionals
No sub-tree pools or root-resizing
Any errors are my own
*/
public class PersistentHashMap extends APersistentMap implements IEditableCollection, IObj, IMapIterable {
final int count;
final INode root;
final boolean hasNull;
final Object nullValue;
final IPersistentMap _meta;
final public static PersistentHashMap EMPTY = new PersistentHashMap(0, null, false, null);
final private static Object NOT_FOUND = new Object();
static public IPersistentMap create(Map other){
ITransientMap ret = EMPTY.asTransient();
for(Object o : other.entrySet())
{
Map.Entry e = (Entry) o;
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret.persistent();
}
/*
* @param init {key1,val1,key2,val2,...}
*/
public static PersistentHashMap create(Object... init){
ITransientMap ret = EMPTY.asTransient();
for(int i = 0; i < init.length; i += 2)
{
ret = ret.assoc(init[i], init[i + 1]);
}
return (PersistentHashMap) ret.persistent();
}
public static PersistentHashMap createWithCheck(Object... init){
ITransientMap ret = EMPTY.asTransient();
for(int i = 0; i < init.length; i += 2)
{
ret = ret.assoc(init[i], init[i + 1]);
if(ret.count() != i/2 + 1)
throw new IllegalArgumentException("Duplicate key: " + init[i]);
}
return (PersistentHashMap) ret.persistent();
}
static public PersistentHashMap create(ISeq items){
ITransientMap ret = EMPTY.asTransient();
for(; items != null; items = items.next().next())
{
if(items.next() == null)
throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
ret = ret.assoc(items.first(), RT.second(items));
}
return (PersistentHashMap) ret.persistent();
}
static public PersistentHashMap createWithCheck(ISeq items){
ITransientMap ret = EMPTY.asTransient();
for(int i=0; items != null; items = items.next().next(), ++i)
{
if(items.next() == null)
throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
ret = ret.assoc(items.first(), RT.second(items));
if(ret.count() != i + 1)
throw new IllegalArgumentException("Duplicate key: " + items.first());
}
return (PersistentHashMap) ret.persistent();
}
/*
* @param init {key1,val1,key2,val2,...}
*/
public static PersistentHashMap create(IPersistentMap meta, Object... init){
return create(init).withMeta(meta);
}
PersistentHashMap(int count, INode root, boolean hasNull, Object nullValue){
this.count = count;
this.root = root;
this.hasNull = hasNull;
this.nullValue = nullValue;
this._meta = null;
}
public PersistentHashMap(IPersistentMap meta, int count, INode root, boolean hasNull, Object nullValue){
this._meta = meta;
this.count = count;
this.root = root;
this.hasNull = hasNull;
this.nullValue = nullValue;
}
static int hash(Object k){
return Util.hasheq(k);
}
public boolean containsKey(Object key){
if(key == null)
return hasNull;
return (root != null) ? root.find(0, hash(key), key, NOT_FOUND) != NOT_FOUND : false;
}
public IMapEntry entryAt(Object key){
if(key == null)
return hasNull ? new MapEntry(null, nullValue) : null;
return (root != null) ? root.find(0, hash(key), key) : null;
}
public IPersistentMap assoc(Object key, Object val){
if(key == null) {
if(hasNull && val == nullValue)
return this;
return new PersistentHashMap(meta(), hasNull ? count : count + 1, root, true, val);
}
Box addedLeaf = new Box(null);
INode newroot = (root == null ? BitmapIndexedNode.EMPTY : root)
.assoc(0, hash(key), key, val, addedLeaf);
if(newroot == root)
return this;
return new PersistentHashMap(meta(), addedLeaf.val == null ? count : count + 1, newroot, hasNull, nullValue);
}
public Object valAt(Object key, Object notFound){
if(key == null)
return hasNull ? nullValue : notFound;
return root != null ? root.find(0, hash(key), key, notFound) : notFound;
}
public Object valAt(Object key){
return valAt(key, null);
}
public IPersistentMap assocEx(Object key, Object val) {
if(containsKey(key))
throw Util.runtimeException("Key already present");
return assoc(key, val);
}
public IPersistentMap without(Object key){
if(key == null)
return hasNull ? new PersistentHashMap(meta(), count - 1, root, false, null) : this;
if(root == null)
return this;
INode newroot = root.without(0, hash(key), key);
if(newroot == root)
return this;
return new PersistentHashMap(meta(), count - 1, newroot, hasNull, nullValue);
}
static final Iterator EMPTY_ITER = new Iterator(){
public boolean hasNext(){
return false;
}
public Object next(){
throw new NoSuchElementException();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
private Iterator iterator(final IFn f){
final Iterator rootIter = (root == null) ? EMPTY_ITER : root.iterator(f);
if(hasNull) {
return new Iterator() {
private boolean seen = false;
public boolean hasNext() {
if (!seen)
return true;
else
return rootIter.hasNext();
}
public Object next(){
if (!seen) {
seen = true;
return f.invoke(null, nullValue);
} else
return rootIter.next();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
else
return rootIter;
}
public Iterator iterator(){
return iterator(APersistentMap.MAKE_ENTRY);
}
public Iterator keyIterator(){
return iterator(APersistentMap.MAKE_KEY);
}
public Iterator valIterator(){
return iterator(APersistentMap.MAKE_VAL);
}
public Object kvreduce(IFn f, Object init){
init = hasNull?f.invoke(init,null,nullValue):init;
if(RT.isReduced(init))
return ((IDeref)init).deref();
if(root != null){
init = root.kvreduce(f,init);
if(RT.isReduced(init))
return ((IDeref)init).deref();
else
return init;
}
return init;
}
public Object fold(long n, final IFn combinef, final IFn reducef,
IFn fjinvoke, final IFn fjtask, final IFn fjfork, final IFn fjjoin){
//we are ignoring n for now
Callable top = new Callable(){
public Object call() throws Exception{
Object ret = combinef.invoke();
if(root != null)
ret = combinef.invoke(ret, root.fold(combinef,reducef,fjtask,fjfork,fjjoin));
return hasNull?
combinef.invoke(ret,reducef.invoke(combinef.invoke(),null,nullValue))
:ret;
}
};
return fjinvoke.invoke(top);
}
public int count(){
return count;
}
public ISeq seq(){
ISeq s = root != null ? root.nodeSeq() : null;
return hasNull ? new Cons(new MapEntry(null, nullValue), s) : s;
}
public IPersistentCollection empty(){
return EMPTY.withMeta(meta());
}
static int mask(int hash, int shift){
//return ((hash << shift) >>> 27);// & 0x01f;
return (hash >>> shift) & 0x01f;
}
public PersistentHashMap withMeta(IPersistentMap meta){
return new PersistentHashMap(meta, count, root, hasNull, nullValue);
}
public TransientHashMap asTransient() {
return new TransientHashMap(this);
}
public IPersistentMap meta(){
return _meta;
}
static final class TransientHashMap extends ATransientMap {
final AtomicReference edit;
volatile INode root;
volatile int count;
volatile boolean hasNull;
volatile Object nullValue;
final Box leafFlag = new Box(null);
TransientHashMap(PersistentHashMap m) {
this(new AtomicReference(Thread.currentThread()), m.root, m.count, m.hasNull, m.nullValue);
}
TransientHashMap(AtomicReference edit, INode root, int count, boolean hasNull, Object nullValue) {
this.edit = edit;
this.root = root;
this.count = count;
this.hasNull = hasNull;
this.nullValue = nullValue;
}
ITransientMap doAssoc(Object key, Object val) {
if (key == null) {
if (this.nullValue != val)
this.nullValue = val;
if (!hasNull) {
this.count++;
this.hasNull = true;
}
return this;
}
// Box leafFlag = new Box(null);
leafFlag.val = null;
INode n = (root == null ? BitmapIndexedNode.EMPTY : root)
.assoc(edit, 0, hash(key), key, val, leafFlag);
if (n != this.root)
this.root = n;
if(leafFlag.val != null) this.count++;
return this;
}
ITransientMap doWithout(Object key) {
if (key == null) {
if (!hasNull) return this;
hasNull = false;
nullValue = null;
this.count--;
return this;
}
if (root == null) return this;
// Box leafFlag = new Box(null);
leafFlag.val = null;
INode n = root.without(edit, 0, hash(key), key, leafFlag);
if (n != root)
this.root = n;
if(leafFlag.val != null) this.count--;
return this;
}
IPersistentMap doPersistent() {
edit.set(null);
return new PersistentHashMap(count, root, hasNull, nullValue);
}
Object doValAt(Object key, Object notFound) {
if (key == null) {
if (hasNull) {
return nullValue;
} else {
return notFound;
}
}
if (root == null) {
return notFound;
}
return root.find(0, hash(key), key, notFound);
}
int doCount() {
return count;
}
void ensureEditable(){
if(edit.get() == null)
throw new IllegalAccessError("Transient used after persistent! call");
}
}
static interface INode extends Serializable {
INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf);
INode without(int shift, int hash, Object key);
IMapEntry find(int shift, int hash, Object key);
Object find(int shift, int hash, Object key, Object notFound);
ISeq nodeSeq();
INode assoc(AtomicReference edit, int shift, int hash, Object key, Object val, Box addedLeaf);
INode without(AtomicReference edit, int shift, int hash, Object key, Box removedLeaf);
public Object kvreduce(IFn f, Object init);
Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin);
// returns the result of (f [k v]) for each iterated element
Iterator iterator(IFn f);
}
final static class ArrayNode implements INode{
int count;
final INode[] array;
final AtomicReference edit;
ArrayNode(AtomicReference edit, int count, INode[] array){
this.array = array;
this.edit = edit;
this.count = count;
}
public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
int idx = mask(hash, shift);
INode node = array[idx];
if(node == null)
return new ArrayNode(null, count + 1, cloneAndSet(array, idx, BitmapIndexedNode.EMPTY.assoc(shift + 5, hash, key, val, addedLeaf)));
INode n = node.assoc(shift + 5, hash, key, val, addedLeaf);
if(n == node)
return this;
return new ArrayNode(null, count, cloneAndSet(array, idx, n));
}
public INode without(int shift, int hash, Object key){
int idx = mask(hash, shift);
INode node = array[idx];
if(node == null)
return this;
INode n = node.without(shift + 5, hash, key);
if(n == node)
return this;
if (n == null) {
if (count <= 8) // shrink
return pack(null, idx);
return new ArrayNode(null, count - 1, cloneAndSet(array, idx, n));
} else
return new ArrayNode(null, count, cloneAndSet(array, idx, n));
}
public IMapEntry find(int shift, int hash, Object key){
int idx = mask(hash, shift);
INode node = array[idx];
if(node == null)
return null;
return node.find(shift + 5, hash, key);
}
public Object find(int shift, int hash, Object key, Object notFound){
int idx = mask(hash, shift);
INode node = array[idx];
if(node == null)
return notFound;
return node.find(shift + 5, hash, key, notFound);
}
public ISeq nodeSeq(){
return Seq.create(array);
}
public Iterator iterator(IFn f){
return new Iter(array, f);
}
public Object kvreduce(IFn f, Object init){
for(INode node : array){
if(node != null){
init = node.kvreduce(f,init);
if(RT.isReduced(init))
return init;
}
}
return init;
}
public Object fold(final IFn combinef, final IFn reducef,
final IFn fjtask, final IFn fjfork, final IFn fjjoin){
List tasks = new ArrayList();
for(final INode node : array){
if(node != null){
tasks.add(new Callable(){
public Object call() throws Exception{
return node.fold(combinef, reducef, fjtask, fjfork, fjjoin);
}
});
}
}
return foldTasks(tasks,combinef,fjtask,fjfork,fjjoin);
}
static public Object foldTasks(List tasks, final IFn combinef,
final IFn fjtask, final IFn fjfork, final IFn fjjoin){
if(tasks.isEmpty())
return combinef.invoke();
if(tasks.size() == 1){
Object ret = null;
try
{
return tasks.get(0).call();
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
}
List t1 = tasks.subList(0,tasks.size()/2);
final List t2 = tasks.subList(tasks.size()/2, tasks.size());
Object forked = fjfork.invoke(fjtask.invoke(new Callable() {
public Object call() throws Exception{
return foldTasks(t2,combinef,fjtask,fjfork,fjjoin);
}
}));
return combinef.invoke(foldTasks(t1,combinef,fjtask,fjfork,fjjoin),fjjoin.invoke(forked));
}
private ArrayNode ensureEditable(AtomicReference edit){
if(this.edit == edit)
return this;
return new ArrayNode(edit, count, this.array.clone());
}
private ArrayNode editAndSet(AtomicReference edit, int i, INode n){
ArrayNode editable = ensureEditable(edit);
editable.array[i] = n;
return editable;
}
private INode pack(AtomicReference edit, int idx) {
Object[] newArray = new Object[2*(count - 1)];
int j = 1;
int bitmap = 0;
for(int i = 0; i < idx; i++)
if (array[i] != null) {
newArray[j] = array[i];
bitmap |= 1 << i;
j += 2;
}
for(int i = idx + 1; i < array.length; i++)
if (array[i] != null) {
newArray[j] = array[i];
bitmap |= 1 << i;
j += 2;
}
return new BitmapIndexedNode(edit, bitmap, newArray);
}
public INode assoc(AtomicReference edit, int shift, int hash, Object key, Object val, Box addedLeaf){
int idx = mask(hash, shift);
INode node = array[idx];
if(node == null) {
ArrayNode editable = editAndSet(edit, idx, BitmapIndexedNode.EMPTY.assoc(edit, shift + 5, hash, key, val, addedLeaf));
editable.count++;
return editable;
}
INode n = node.assoc(edit, shift + 5, hash, key, val, addedLeaf);
if(n == node)
return this;
return editAndSet(edit, idx, n);
}
public INode without(AtomicReference edit, int shift, int hash, Object key, Box removedLeaf){
int idx = mask(hash, shift);
INode node = array[idx];
if(node == null)
return this;
INode n = node.without(edit, shift + 5, hash, key, removedLeaf);
if(n == node)
return this;
if(n == null) {
if (count <= 8) // shrink
return pack(edit, idx);
ArrayNode editable = editAndSet(edit, idx, n);
editable.count--;
return editable;
}
return editAndSet(edit, idx, n);
}
static class Seq extends ASeq {
final INode[] nodes;
final int i;
final ISeq s;
static ISeq create(INode[] nodes) {
return create(null, nodes, 0, null);
}
private static ISeq create(IPersistentMap meta, INode[] nodes, int i, ISeq s) {
if (s != null)
return new Seq(meta, nodes, i, s);
for(int j = i; j < nodes.length; j++)
if (nodes[j] != null) {
ISeq ns = nodes[j].nodeSeq();
if (ns != null)
return new Seq(meta, nodes, j + 1, ns);
}
return null;
}
private Seq(IPersistentMap meta, INode[] nodes, int i, ISeq s) {
super(meta);
this.nodes = nodes;
this.i = i;
this.s = s;
}
public Obj withMeta(IPersistentMap meta) {
return new Seq(meta, nodes, i, s);
}
public Object first() {
return s.first();
}
public ISeq next() {
return create(null, nodes, i, s.next());
}
}
static class Iter implements Iterator {
private final INode[] array;
private final IFn f;
private int i = 0;
private Iterator nestedIter;
private Iter(INode[] array, IFn f){
this.array = array;
this.f = f;
}
public boolean hasNext(){
while(true)
{
if(nestedIter != null) {
if(nestedIter.hasNext())
return true;
else
nestedIter = null;
}
if(i < array.length)
{
INode node = array[i++];
if (node != null)
nestedIter = node.iterator(f);
}
else
return false;
}
}
public Object next(){
if(hasNext())
return nestedIter.next();
else
throw new NoSuchElementException();
}
public void remove(){
throw new UnsupportedOperationException();
}
}
}
final static class BitmapIndexedNode implements INode{
static final BitmapIndexedNode EMPTY = new BitmapIndexedNode(null, 0, new Object[0]);
int bitmap;
Object[] array;
final AtomicReference edit;
final int index(int bit){
return Integer.bitCount(bitmap & (bit - 1));
}
BitmapIndexedNode(AtomicReference edit, int bitmap, Object[] array){
this.bitmap = bitmap;
this.array = array;
this.edit = edit;
}
public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
int bit = bitpos(hash, shift);
int idx = index(bit);
if((bitmap & bit) != 0) {
Object keyOrNull = array[2*idx];
Object valOrNode = array[2*idx+1];
if(keyOrNull == null) {
INode n = ((INode) valOrNode).assoc(shift + 5, hash, key, val, addedLeaf);
if(n == valOrNode)
return this;
return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, n));
}
if(Util.equiv(key, keyOrNull)) {
if(val == valOrNode)
return this;
return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, val));
}
addedLeaf.val = addedLeaf;
return new BitmapIndexedNode(null, bitmap,
cloneAndSet(array,
2*idx, null,
2*idx+1, createNode(shift + 5, keyOrNull, valOrNode, hash, key, val)));
} else {
int n = Integer.bitCount(bitmap);
if(n >= 16) {
INode[] nodes = new INode[32];
int jdx = mask(hash, shift);
nodes[jdx] = EMPTY.assoc(shift + 5, hash, key, val, addedLeaf);
int j = 0;
for(int i = 0; i < 32; i++)
if(((bitmap >>> i) & 1) != 0) {
if (array[j] == null)
nodes[i] = (INode) array[j+1];
else
nodes[i] = EMPTY.assoc(shift + 5, hash(array[j]), array[j], array[j+1], addedLeaf);
j += 2;
}
return new ArrayNode(null, n + 1, nodes);
} else {
Object[] newArray = new Object[2*(n+1)];
System.arraycopy(array, 0, newArray, 0, 2*idx);
newArray[2*idx] = key;
addedLeaf.val = addedLeaf;
newArray[2*idx+1] = val;
System.arraycopy(array, 2*idx, newArray, 2*(idx+1), 2*(n-idx));
return new BitmapIndexedNode(null, bitmap | bit, newArray);
}
}
}
public INode without(int shift, int hash, Object key){
int bit = bitpos(hash, shift);
if((bitmap & bit) == 0)
return this;
int idx = index(bit);
Object keyOrNull = array[2*idx];
Object valOrNode = array[2*idx+1];
if(keyOrNull == null) {
INode n = ((INode) valOrNode).without(shift + 5, hash, key);
if (n == valOrNode)
return this;
if (n != null)
return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, n));
if (bitmap == bit)
return null;
return new BitmapIndexedNode(null, bitmap ^ bit, removePair(array, idx));
}
if(Util.equiv(key, keyOrNull))
// TODO: collapse
return new BitmapIndexedNode(null, bitmap ^ bit, removePair(array, idx));
return this;
}
public IMapEntry find(int shift, int hash, Object key){
int bit = bitpos(hash, shift);
if((bitmap & bit) == 0)
return null;
int idx = index(bit);
Object keyOrNull = array[2*idx];
Object valOrNode = array[2*idx+1];
if(keyOrNull == null)
return ((INode) valOrNode).find(shift + 5, hash, key);
if(Util.equiv(key, keyOrNull))
return new MapEntry(keyOrNull, valOrNode);
return null;
}
public Object find(int shift, int hash, Object key, Object notFound){
int bit = bitpos(hash, shift);
if((bitmap & bit) == 0)
return notFound;
int idx = index(bit);
Object keyOrNull = array[2*idx];
Object valOrNode = array[2*idx+1];
if(keyOrNull == null)
return ((INode) valOrNode).find(shift + 5, hash, key, notFound);
if(Util.equiv(key, keyOrNull))
return valOrNode;
return notFound;
}
public ISeq nodeSeq(){
return NodeSeq.create(array);
}
public Iterator iterator(IFn f){
return new NodeIter(array, f);
}
public Object kvreduce(IFn f, Object init){
return NodeSeq.kvreduce(array,f,init);
}
public Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin){
return NodeSeq.kvreduce(array, reducef, combinef.invoke());
}
private BitmapIndexedNode ensureEditable(AtomicReference edit){
if(this.edit == edit)
return this;
int n = Integer.bitCount(bitmap);
Object[] newArray = new Object[n >= 0 ? 2*(n+1) : 4]; // make room for next assoc
System.arraycopy(array, 0, newArray, 0, 2*n);
return new BitmapIndexedNode(edit, bitmap, newArray);
}
private BitmapIndexedNode editAndSet(AtomicReference edit, int i, Object a) {
BitmapIndexedNode editable = ensureEditable(edit);
editable.array[i] = a;
return editable;
}
private BitmapIndexedNode editAndSet(AtomicReference edit, int i, Object a, int j, Object b) {
BitmapIndexedNode editable = ensureEditable(edit);
editable.array[i] = a;
editable.array[j] = b;
return editable;
}
private BitmapIndexedNode editAndRemovePair(AtomicReference edit, int bit, int i) {
if (bitmap == bit)
return null;
BitmapIndexedNode editable = ensureEditable(edit);
editable.bitmap ^= bit;
System.arraycopy(editable.array, 2*(i+1), editable.array, 2*i, editable.array.length - 2*(i+1));
editable.array[editable.array.length - 2] = null;
editable.array[editable.array.length - 1] = null;
return editable;
}
public INode assoc(AtomicReference edit, int shift, int hash, Object key, Object val, Box addedLeaf){
int bit = bitpos(hash, shift);
int idx = index(bit);
if((bitmap & bit) != 0) {
Object keyOrNull = array[2*idx];
Object valOrNode = array[2*idx+1];
if(keyOrNull == null) {
INode n = ((INode) valOrNode).assoc(edit, shift + 5, hash, key, val, addedLeaf);
if(n == valOrNode)
return this;
return editAndSet(edit, 2*idx+1, n);
}
if(Util.equiv(key, keyOrNull)) {
if(val == valOrNode)
return this;
return editAndSet(edit, 2*idx+1, val);
}
addedLeaf.val = addedLeaf;
return editAndSet(edit, 2*idx, null, 2*idx+1,
createNode(edit, shift + 5, keyOrNull, valOrNode, hash, key, val));
} else {
int n = Integer.bitCount(bitmap);
if(n*2 < array.length) {
addedLeaf.val = addedLeaf;
BitmapIndexedNode editable = ensureEditable(edit);
System.arraycopy(editable.array, 2*idx, editable.array, 2*(idx+1), 2*(n-idx));
editable.array[2*idx] = key;
editable.array[2*idx+1] = val;
editable.bitmap |= bit;
return editable;
}
if(n >= 16) {
INode[] nodes = new INode[32];
int jdx = mask(hash, shift);
nodes[jdx] = EMPTY.assoc(edit, shift + 5, hash, key, val, addedLeaf);
int j = 0;
for(int i = 0; i < 32; i++)
if(((bitmap >>> i) & 1) != 0) {
if (array[j] == null)
nodes[i] = (INode) array[j+1];
else
nodes[i] = EMPTY.assoc(edit, shift + 5, hash(array[j]), array[j], array[j+1], addedLeaf);
j += 2;
}
return new ArrayNode(edit, n + 1, nodes);
} else {
Object[] newArray = new Object[2*(n+4)];
System.arraycopy(array, 0, newArray, 0, 2*idx);
newArray[2*idx] = key;
addedLeaf.val = addedLeaf;
newArray[2*idx+1] = val;
System.arraycopy(array, 2*idx, newArray, 2*(idx+1), 2*(n-idx));
BitmapIndexedNode editable = ensureEditable(edit);
editable.array = newArray;
editable.bitmap |= bit;
return editable;
}
}
}
public INode without(AtomicReference edit, int shift, int hash, Object key, Box removedLeaf){
int bit = bitpos(hash, shift);
if((bitmap & bit) == 0)
return this;
int idx = index(bit);
Object keyOrNull = array[2*idx];
Object valOrNode = array[2*idx+1];
if(keyOrNull == null) {
INode n = ((INode) valOrNode).without(edit, shift + 5, hash, key, removedLeaf);
if (n == valOrNode)
return this;
if (n != null)
return editAndSet(edit, 2*idx+1, n);
if (bitmap == bit)
return null;
return editAndRemovePair(edit, bit, idx);
}
if(Util.equiv(key, keyOrNull)) {
removedLeaf.val = removedLeaf;
// TODO: collapse
return editAndRemovePair(edit, bit, idx);
}
return this;
}
}
final static class HashCollisionNode implements INode{
final int hash;
int count;
Object[] array;
final AtomicReference edit;
int[] keys;
HashCollisionNode(AtomicReference edit, int hash, int count, Object... array){
this.edit = edit;
this.hash = hash;
this.count = count;
this.array = array;
}
void ensureKeys() {
if (keys == null) {
keys = new int[array.length];
for(int i = 0; i < array.length; i+=2) {
keys[i] = Util.hash(array[i]);
}
}
}
public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
if(hash == this.hash) {
int idx = findIndex(key);
if(idx != -1) {
if(array[idx + 1] == val)
return this;
return new HashCollisionNode(null, hash, count, cloneAndSet(array, idx + 1, val));
}
Object[] newArray = new Object[2 * (count + 1)];
System.arraycopy(array, 0, newArray, 0, 2 * count);
newArray[2 * count] = key;
newArray[2 * count + 1] = val;
addedLeaf.val = addedLeaf;
return new HashCollisionNode(edit, hash, count + 1, newArray);
}
// nest it in a bitmap node
return new BitmapIndexedNode(null, bitpos(this.hash, shift), new Object[] {null, this})
.assoc(shift, hash, key, val, addedLeaf);
}
public INode without(int shift, int hash, Object key){
int idx = findIndex(key);
if(idx == -1)
return this;
if(count == 1)
return null;
return new HashCollisionNode(null, hash, count - 1, removePair(array, idx/2));
}
public IMapEntry find(int shift, int hash, Object key){
int idx = findIndex(key);
if(idx < 0)
return null;
if(Util.equiv(key, array[idx]))
return new MapEntry(array[idx], array[idx+1]);
return null;
}
public Object find(int shift, int hash, Object key, Object notFound){
int idx = findIndex(key);
if(idx < 0)
return notFound;
if(Util.equiv(key, array[idx]))
return array[idx+1];
return notFound;
}
public ISeq nodeSeq(){
return NodeSeq.create(array);
}
public Iterator iterator(IFn f){
return new NodeIter(array, f);
}
public Object kvreduce(IFn f, Object init){
return NodeSeq.kvreduce(array,f,init);
}
public Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin){
return NodeSeq.kvreduce(array, reducef, combinef.invoke());
}
public int findIndex(Object key){
ensureKeys();
int hash = Util.hash(key);
for(int i = 0; i < 2*count; i+=2)
{
if(keys[i] == hash && Util.equiv(key, array[i]))
return i;
}
return -1;
}
private HashCollisionNode ensureEditable(AtomicReference edit){
if(this.edit == edit)
return this;
Object[] newArray = new Object[2*(count+1)]; // make room for next assoc
System.arraycopy(array, 0, newArray, 0, 2*count);
return new HashCollisionNode(edit, hash, count, newArray);
}
private HashCollisionNode ensureEditable(AtomicReference edit, int count, Object[] array){
if(this.edit == edit) {
this.array = array;
this.count = count;
this.keys = null;
return this;
}
return new HashCollisionNode(edit, hash, count, array);
}
private HashCollisionNode editAndSet(AtomicReference edit, int i, Object a) {
HashCollisionNode editable = ensureEditable(edit);
editable.array[i] = a;
return editable;
}
private HashCollisionNode editAndSet(AtomicReference edit, int i, Object a, int j, Object b) {
HashCollisionNode editable = ensureEditable(edit);
editable.array[i] = a;
editable.array[j] = b;
return editable;
}
public INode assoc(AtomicReference edit, int shift, int hash, Object key, Object val, Box addedLeaf){
if(hash == this.hash) {
int idx = findIndex(key);
if(idx != -1) {
if(array[idx + 1] == val)
return this;
return editAndSet(edit, idx+1, val);
}
if (array.length > 2*count) {
addedLeaf.val = addedLeaf;
HashCollisionNode editable = editAndSet(edit, 2*count, key, 2*count+1, val);
editable.count++;
return editable;
}
Object[] newArray = new Object[array.length + 2];
System.arraycopy(array, 0, newArray, 0, array.length);
newArray[array.length] = key;
newArray[array.length + 1] = val;
addedLeaf.val = addedLeaf;
return ensureEditable(edit, count + 1, newArray);
}
// nest it in a bitmap node
return new BitmapIndexedNode(edit, bitpos(this.hash, shift), new Object[] {null, this, null, null})
.assoc(edit, shift, hash, key, val, addedLeaf);
}
public INode without(AtomicReference edit, int shift, int hash, Object key, Box removedLeaf){
int idx = findIndex(key);
if(idx == -1)
return this;
removedLeaf.val = removedLeaf;
if(count == 1)
return null;
HashCollisionNode editable = ensureEditable(edit);
editable.array[idx] = editable.array[2*count-2];
editable.array[idx+1] = editable.array[2*count-1];
editable.array[2*count-2] = editable.array[2*count-1] = null;
editable.count--;
return editable;
}
}
/*
public static void main(String[] args){
try
{
ArrayList words = new ArrayList();
Scanner s = new Scanner(new File(args[0]));
s.useDelimiter(Pattern.compile("\\W"));
while(s.hasNext())
{
String word = s.next();
words.add(word);
}
System.out.println("words: " + words.size());
IPersistentMap map = PersistentHashMap.EMPTY;
//IPersistentMap map = new PersistentTreeMap();
//Map ht = new Hashtable();
Map ht = new HashMap();
Random rand;
System.out.println("Building map");
long startTime = System.nanoTime();
for(Object word5 : words)
{
map = map.assoc(word5, word5);
}
rand = new Random(42);
IPersistentMap snapshotMap = map;
for(int i = 0; i < words.size() / 200; i++)
{
map = map.without(words.get(rand.nextInt(words.size() / 2)));
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println("count = " + map.count() + ", time: " + estimatedTime / 1000000);
System.out.println("Building ht");
startTime = System.nanoTime();
for(Object word1 : words)
{
ht.put(word1, word1);
}
rand = new Random(42);
for(int i = 0; i < words.size() / 200; i++)
{
ht.remove(words.get(rand.nextInt(words.size() / 2)));
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("count = " + ht.size() + ", time: " + estimatedTime / 1000000);
System.out.println("map lookup");
startTime = System.nanoTime();
int c = 0;
for(Object word2 : words)
{
if(!map.contains(word2))
++c;
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
System.out.println("ht lookup");
startTime = System.nanoTime();
c = 0;
for(Object word3 : words)
{
if(!ht.containsKey(word3))
++c;
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
System.out.println("snapshotMap lookup");
startTime = System.nanoTime();
c = 0;
for(Object word4 : words)
{
if(!snapshotMap.contains(word4))
++c;
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
}
catch(FileNotFoundException e)
{
e.printStackTrace();
}
}
*/
private static INode[] cloneAndSet(INode[] array, int i, INode a) {
INode[] clone = array.clone();
clone[i] = a;
return clone;
}
private static Object[] cloneAndSet(Object[] array, int i, Object a) {
Object[] clone = array.clone();
clone[i] = a;
return clone;
}
private static Object[] cloneAndSet(Object[] array, int i, Object a, int j, Object b) {
Object[] clone = array.clone();
clone[i] = a;
clone[j] = b;
return clone;
}
private static Object[] removePair(Object[] array, int i) {
Object[] newArray = new Object[array.length - 2];
System.arraycopy(array, 0, newArray, 0, 2*i);
System.arraycopy(array, 2*(i+1), newArray, 2*i, newArray.length - 2*i);
return newArray;
}
private static INode createNode(int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
int key1hash = hash(key1);
if(key1hash == key2hash)
return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
Box addedLeaf = new Box(null);
AtomicReference edit = new AtomicReference();
return BitmapIndexedNode.EMPTY
.assoc(edit, shift, key1hash, key1, val1, addedLeaf)
.assoc(edit, shift, key2hash, key2, val2, addedLeaf);
}
private static INode createNode(AtomicReference edit, int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
int key1hash = hash(key1);
if(key1hash == key2hash)
return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
Box addedLeaf = new Box(null);
return BitmapIndexedNode.EMPTY
.assoc(edit, shift, key1hash, key1, val1, addedLeaf)
.assoc(edit, shift, key2hash, key2, val2, addedLeaf);
}
private static int bitpos(int hash, int shift){
return 1 << mask(hash, shift);
}
static final class NodeIter implements Iterator {
private static final Object NULL = new Object();
final Object[] array;
final IFn f;
private int i = 0;
private Object nextEntry = NULL;
private Iterator nextIter;
NodeIter(Object[] array, IFn f){
this.array = array;
this.f = f;
}
private boolean advance(){
while (i= ((ArraySeq)args).i; --i)
ret = (IPersistentList) ret.cons(argsarray[i]);
return ret;
}
LinkedList list = new LinkedList();
for(ISeq s = RT.seq(args); s != null; s = s.next())
list.add(s.first());
return create(list);
}
};
final public static EmptyList EMPTY = new EmptyList(null);
public PersistentList(Object first){
this._first = first;
this._rest = null;
this._count = 1;
}
PersistentList(IPersistentMap meta, Object _first, IPersistentList _rest, int _count){
super(meta);
this._first = _first;
this._rest = _rest;
this._count = _count;
}
public static IPersistentList create(List init){
IPersistentList ret = EMPTY;
for(ListIterator i = init.listIterator(init.size()); i.hasPrevious();)
{
ret = (IPersistentList) ret.cons(i.previous());
}
return ret;
}
public Object first(){
return _first;
}
public ISeq next(){
if(_count == 1)
return null;
return (ISeq) _rest;
}
public Object peek(){
return first();
}
public IPersistentList pop(){
if(_rest == null)
return EMPTY.withMeta(_meta);
return _rest;
}
public int count(){
return _count;
}
public PersistentList cons(Object o){
return new PersistentList(meta(), o, this, _count + 1);
}
public IPersistentCollection empty(){
return EMPTY.withMeta(meta());
}
public PersistentList withMeta(IPersistentMap meta){
if(meta != _meta)
return new PersistentList(meta, _first, _rest, _count);
return this;
}
public Object reduce(IFn f) {
Object ret = first();
for(ISeq s = next(); s != null; s = s.next()) {
ret = f.invoke(ret, s.first());
if (RT.isReduced(ret)) return ((IDeref)ret).deref();;
}
return ret;
}
public Object reduce(IFn f, Object start) {
Object ret = f.invoke(start, first());
for(ISeq s = next(); s != null; s = s.next()) {
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
ret = f.invoke(ret, s.first());
}
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
return ret;
}
public static class EmptyList extends Obj implements IPersistentList, List, ISeq, Counted, IHashEq{
public static final int hasheq = Murmur3.hashOrdered(Collections.EMPTY_LIST);
public int hashCode(){
return 1;
}
public int hasheq(){
return hasheq;
}
public boolean equals(Object o) {
return (o instanceof Sequential || o instanceof List) && RT.seq(o) == null;
}
public boolean equiv(Object o){
return equals(o);
}
EmptyList(IPersistentMap meta){
super(meta);
}
public Object first() {
return null;
}
public ISeq next() {
return null;
}
public ISeq more() {
return this;
}
public PersistentList cons(Object o){
return new PersistentList(meta(), o, null, 1);
}
public IPersistentCollection empty(){
return this;
}
public EmptyList withMeta(IPersistentMap meta){
if(meta != meta())
return new EmptyList(meta);
return this;
}
public Object peek(){
return null;
}
public IPersistentList pop(){
throw new IllegalStateException("Can't pop empty list");
}
public int count(){
return 0;
}
public ISeq seq(){
return null;
}
public int size(){
return 0;
}
public boolean isEmpty(){
return true;
}
public boolean contains(Object o){
return false;
}
public Iterator iterator(){
return new Iterator(){
public boolean hasNext(){
return false;
}
public Object next(){
throw new NoSuchElementException();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public Object[] toArray(){
return RT.EMPTY_ARRAY;
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection collection){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection collection){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection collection){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection collection){
return collection.isEmpty();
}
public Object[] toArray(Object[] objects){
if(objects.length > 0)
objects[0] = null;
return objects;
}
//////////// List stuff /////////////////
private List reify(){
return Collections.unmodifiableList(new ArrayList(this));
}
public List subList(int fromIndex, int toIndex){
return reify().subList(fromIndex, toIndex);
}
public Object set(int index, Object element){
throw new UnsupportedOperationException();
}
public Object remove(int index){
throw new UnsupportedOperationException();
}
public int indexOf(Object o){
ISeq s = seq();
for(int i = 0; s != null; s = s.next(), i++)
{
if(Util.equiv(s.first(), o))
return i;
}
return -1;
}
public int lastIndexOf(Object o){
return reify().lastIndexOf(o);
}
public ListIterator listIterator(){
return reify().listIterator();
}
public ListIterator listIterator(int index){
return reify().listIterator(index);
}
public Object get(int index){
return RT.nth(this, index);
}
public void add(int index, Object element){
throw new UnsupportedOperationException();
}
public boolean addAll(int index, Collection c){
throw new UnsupportedOperationException();
}
}
}
================================================
FILE: src/jvm/clojure/lang/PersistentQueue.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
//import java.util.concurrent.ConcurrentLinkedQueue;
/**
* conses onto rear, peeks/pops from front
* See Okasaki's Batched Queues
* This differs in that it uses a PersistentVector as the rear, which is in-order,
* so no reversing or suspensions required for persistent use
*/
public class PersistentQueue extends Obj implements IPersistentList, Collection, Counted, IHashEq{
final public static PersistentQueue EMPTY = new PersistentQueue(null, 0, null, null);
//*
final int cnt;
final ISeq f;
final PersistentVector r;
//static final int INITIAL_REAR_SIZE = 4;
int _hash = -1;
int _hasheq = -1;
PersistentQueue(IPersistentMap meta, int cnt, ISeq f, PersistentVector r){
super(meta);
this.cnt = cnt;
this.f = f;
this.r = r;
}
public boolean equiv(Object obj){
if(!(obj instanceof Sequential))
return false;
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
if(ms == null || !Util.equiv(s.first(), ms.first()))
return false;
}
return ms == null;
}
public boolean equals(Object obj){
if(!(obj instanceof Sequential))
return false;
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
if(ms == null || !Util.equals(s.first(), ms.first()))
return false;
}
return ms == null;
}
public int hashCode(){
if(_hash == -1)
{
int hash = 1;
for(ISeq s = seq(); s != null; s = s.next())
{
hash = 31 * hash + (s.first() == null ? 0 : Util.hash(s.first()));
}
this._hash = hash;
}
return _hash;
}
public int hasheq() {
if(_hasheq == -1)
{
// int hash = 1;
// for(ISeq s = seq(); s != null; s = s.next())
// {
// hash = 31 * hash + Util.hasheq(s.first());
// }
// this._hasheq = hash;
_hasheq = Murmur3.hashOrdered(this);
}
return _hasheq;
}
public Object peek(){
return RT.first(f);
}
public PersistentQueue pop(){
if(f == null) //hmmm... pop of empty queue -> empty queue?
return this;
//throw new IllegalStateException("popping empty queue");
ISeq f1 = f.next();
PersistentVector r1 = r;
if(f1 == null)
{
f1 = RT.seq(r);
r1 = null;
}
return new PersistentQueue(meta(), cnt - 1, f1, r1);
}
public int count(){
return cnt;
}
public ISeq seq(){
if(f == null)
return null;
return new Seq(f, RT.seq(r));
}
public PersistentQueue cons(Object o){
if(f == null) //empty
return new PersistentQueue(meta(), cnt + 1, RT.list(o), null);
else
return new PersistentQueue(meta(), cnt + 1, f, (r != null ? r : PersistentVector.EMPTY).cons(o));
}
public IPersistentCollection empty(){
return EMPTY.withMeta(meta());
}
public PersistentQueue withMeta(IPersistentMap meta){
return new PersistentQueue(meta, cnt, f, r);
}
static class Seq extends ASeq{
final ISeq f;
final ISeq rseq;
Seq(ISeq f, ISeq rseq){
this.f = f;
this.rseq = rseq;
}
Seq(IPersistentMap meta, ISeq f, ISeq rseq){
super(meta);
this.f = f;
this.rseq = rseq;
}
public Object first(){
return f.first();
}
public ISeq next(){
ISeq f1 = f.next();
ISeq r1 = rseq;
if(f1 == null)
{
if(rseq == null)
return null;
f1 = rseq;
r1 = null;
}
return new Seq(f1, r1);
}
public int count(){
return RT.count(f) + RT.count(rseq);
}
public Seq withMeta(IPersistentMap meta){
return new Seq(meta, f, rseq);
}
}
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(contains(o))
return true;
}
return false;
}
public Object[] toArray(Object[] a){
return RT.seqToPassedArray(seq(), a);
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.next())
{
if(Util.equiv(s.first(), o))
return true;
}
return false;
}
public Iterator iterator(){
return new Iterator(){
private ISeq fseq = f;
private final Iterator riter = r != null ? r.iterator() : null;
public boolean hasNext(){
return ((fseq != null && fseq.seq() != null) || (riter != null && riter.hasNext()));
}
public Object next(){
if(fseq != null)
{
Object ret = fseq.first();
fseq = fseq.next();
return ret;
}
else if(riter != null && riter.hasNext())
return riter.next();
else
throw new NoSuchElementException();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
/*
public static void main(String[] args){
if(args.length != 1)
{
System.err.println("Usage: PersistentQueue n");
return;
}
int n = Integer.parseInt(args[0]);
long startTime, estimatedTime;
Queue list = new LinkedList();
//Queue list = new ConcurrentLinkedQueue();
System.out.println("Queue");
startTime = System.nanoTime();
for(int i = 0; i < n; i++)
{
list.add(i);
list.add(i);
list.remove();
}
for(int i = 0; i < n - 10; i++)
{
list.remove();
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
System.out.println("peek: " + list.peek());
PersistentQueue q = PersistentQueue.EMPTY;
System.out.println("PersistentQueue");
startTime = System.nanoTime();
for(int i = 0; i < n; i++)
{
q = q.cons(i);
q = q.cons(i);
q = q.pop();
}
// IPersistentList lastq = null;
// IPersistentList lastq2;
for(int i = 0; i < n - 10; i++)
{
//lastq2 = lastq;
//lastq = q;
q = q.pop();
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
System.out.println("peek: " + q.peek());
IPersistentList q2 = q;
for(int i = 0; i < 10; i++)
{
q2 = (IPersistentList) q2.cons(i);
}
// for(ISeq s = q.seq();s != null;s = s.rest())
// System.out.println("q: " + s.first().toString());
// for(ISeq s = q2.seq();s != null;s = s.rest())
// System.out.println("q2: " + s.first().toString());
}
*/
}
================================================
FILE: src/jvm/clojure/lang/PersistentStructMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 16, 2007 */
package clojure.lang;
import java.util.Iterator;
import java.util.Map;
import java.io.Serializable;
import java.util.NoSuchElementException;
public class PersistentStructMap extends APersistentMap implements IObj{
public static class Def implements Serializable{
final ISeq keys;
final IPersistentMap keyslots;
Def(ISeq keys, IPersistentMap keyslots){
this.keys = keys;
this.keyslots = keyslots;
}
}
final Def def;
final Object[] vals;
final IPersistentMap ext;
final IPersistentMap _meta;
static public Def createSlotMap(ISeq keys){
if(keys == null)
throw new IllegalArgumentException("Must supply keys");
int c = RT.count(keys);
Object[] v = new Object[2*c];
int i = 0;
for(ISeq s = keys; s != null; s = s.next(), i++)
{
v[2*i] = s.first();
v[2*i+1] = i;
}
return new Def(keys, RT.map(v));
}
static public PersistentStructMap create(Def def, ISeq keyvals){
Object[] vals = new Object[def.keyslots.count()];
IPersistentMap ext = PersistentHashMap.EMPTY;
for(; keyvals != null; keyvals = keyvals.next().next())
{
if(keyvals.next() == null)
throw new IllegalArgumentException(String.format("No value supplied for key: %s", keyvals.first()));
Object k = keyvals.first();
Object v = RT.second(keyvals);
Map.Entry e = def.keyslots.entryAt(k);
if(e != null)
vals[(Integer) e.getValue()] = v;
else
ext = ext.assoc(k, v);
}
return new PersistentStructMap(null, def, vals, ext);
}
static public PersistentStructMap construct(Def def, ISeq valseq){
Object[] vals = new Object[def.keyslots.count()];
IPersistentMap ext = PersistentHashMap.EMPTY;
for(int i = 0; i < vals.length && valseq != null; valseq = valseq.next(), i++)
{
vals[i] = valseq.first();
}
if(valseq != null)
throw new IllegalArgumentException("Too many arguments to struct constructor");
return new PersistentStructMap(null, def, vals, ext);
}
static public IFn getAccessor(final Def def, Object key){
Map.Entry e = def.keyslots.entryAt(key);
if(e != null)
{
final int i = (Integer) e.getValue();
return new AFn(){
public Object invoke(Object arg1) {
PersistentStructMap m = (PersistentStructMap) arg1;
if(m.def != def)
throw Util.runtimeException("Accessor/struct mismatch");
return m.vals[i];
}
};
}
throw new IllegalArgumentException("Not a key of struct");
}
protected PersistentStructMap(IPersistentMap meta, Def def, Object[] vals, IPersistentMap ext){
this._meta = meta;
this.ext = ext;
this.def = def;
this.vals = vals;
}
/**
* Returns a new instance of PersistentStructMap using the given parameters.
* This function is used instead of the PersistentStructMap constructor by
* all methods that return a new PersistentStructMap. This is done so as to
* allow subclasses to return instances of their class from all
* PersistentStructMap methods.
*/
protected PersistentStructMap makeNew(IPersistentMap meta, Def def, Object[] vals, IPersistentMap ext){
return new PersistentStructMap(meta, def, vals, ext);
}
public IObj withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
return makeNew(meta, def, vals, ext);
}
public IPersistentMap meta(){
return _meta;
}
public boolean containsKey(Object key){
return def.keyslots.containsKey(key) || ext.containsKey(key);
}
public IMapEntry entryAt(Object key){
Map.Entry e = def.keyslots.entryAt(key);
if(e != null)
{
return new MapEntry(e.getKey(), vals[(Integer) e.getValue()]);
}
return ext.entryAt(key);
}
public IPersistentMap assoc(Object key, Object val){
Map.Entry e = def.keyslots.entryAt(key);
if(e != null)
{
int i = (Integer) e.getValue();
Object[] newVals = vals.clone();
newVals[i] = val;
return makeNew(_meta, def, newVals, ext);
}
return makeNew(_meta, def, vals, ext.assoc(key, val));
}
public Object valAt(Object key){
Integer i = (Integer) def.keyslots.valAt(key);
if(i != null)
{
return vals[i];
}
return ext.valAt(key);
}
public Object valAt(Object key, Object notFound){
Integer i = (Integer) def.keyslots.valAt(key);
if(i != null)
{
return vals[i];
}
return ext.valAt(key, notFound);
}
public IPersistentMap assocEx(Object key, Object val) {
if(containsKey(key))
throw Util.runtimeException("Key already present");
return assoc(key, val);
}
public IPersistentMap without(Object key) {
Map.Entry e = def.keyslots.entryAt(key);
if(e != null)
throw Util.runtimeException("Can't remove struct key");
IPersistentMap newExt = ext.without(key);
if(newExt == ext)
return this;
return makeNew(_meta, def, vals, newExt);
}
public Iterator iterator(){
return new Iterator(){
private ISeq ks = def.keys;
private Iterator extIter = ext == null ? null : ext.iterator();
public boolean hasNext(){
return ((ks != null && ks.seq() != null) || (extIter != null && extIter.hasNext()));
}
public Object next(){
if(ks != null)
{
Object key = ks.first();
ks = ks.next();
return entryAt(key);
}
else if(extIter != null && extIter.hasNext())
return extIter.next();
else
throw new NoSuchElementException();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public int count(){
return vals.length + RT.count(ext);
}
public ISeq seq(){
return new Seq(null, def.keys, vals, 0, ext);
}
public IPersistentCollection empty(){
return construct(def, null);
}
static class Seq extends ASeq{
final int i;
final ISeq keys;
final Object[] vals;
final IPersistentMap ext;
public Seq(IPersistentMap meta, ISeq keys, Object[] vals, int i, IPersistentMap ext){
super(meta);
this.i = i;
this.keys = keys;
this.vals = vals;
this.ext = ext;
}
public Obj withMeta(IPersistentMap meta){
if(meta != _meta)
return new Seq(meta, keys, vals, i, ext);
return this;
}
public Object first(){
return new MapEntry(keys.first(), vals[i]);
}
public ISeq next(){
if(i + 1 < vals.length)
return new Seq(_meta, keys.next(), vals, i + 1, ext);
return ext.seq();
}
}
}
================================================
FILE: src/jvm/clojure/lang/PersistentTreeMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich May 20, 2006 */
package clojure.lang;
import java.util.*;
/**
* Persistent Red Black Tree
* Note that instances of this class are constant values
* i.e. add/remove etc return new values
*
* See Okasaki, Kahrs, Larsen et al
*/
public class PersistentTreeMap extends APersistentMap implements IObj, Reversible, Sorted{
public final Comparator comp;
public final Node tree;
public final int _count;
final IPersistentMap _meta;
final static public PersistentTreeMap EMPTY = new PersistentTreeMap();
static public IPersistentMap create(Map other){
IPersistentMap ret = EMPTY;
for(Object o : other.entrySet())
{
Map.Entry e = (Entry) o;
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret;
}
public PersistentTreeMap(){
this(RT.DEFAULT_COMPARATOR);
}
public PersistentTreeMap withMeta(IPersistentMap meta){
return new PersistentTreeMap(meta, comp, tree, _count);
}
private PersistentTreeMap(Comparator comp){
this(null, comp);
}
public PersistentTreeMap(IPersistentMap meta, Comparator comp){
this.comp = comp;
this._meta = meta;
tree = null;
_count = 0;
}
PersistentTreeMap(IPersistentMap meta, Comparator comp, Node tree, int _count){
this._meta = meta;
this.comp = comp;
this.tree = tree;
this._count = _count;
}
static public PersistentTreeMap create(ISeq items){
IPersistentMap ret = EMPTY;
for(; items != null; items = items.next().next())
{
if(items.next() == null)
throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
ret = ret.assoc(items.first(), RT.second(items));
}
return (PersistentTreeMap) ret;
}
static public PersistentTreeMap create(Comparator comp, ISeq items){
IPersistentMap ret = new PersistentTreeMap(comp);
for(; items != null; items = items.next().next())
{
if(items.next() == null)
throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
ret = ret.assoc(items.first(), RT.second(items));
}
return (PersistentTreeMap) ret;
}
public boolean containsKey(Object key){
return entryAt(key) != null;
}
public PersistentTreeMap assocEx(Object key, Object val) {
Box found = new Box(null);
Node t = add(tree, key, val, found);
if(t == null) //null == already contains key
{
throw Util.runtimeException("Key already present");
}
return new PersistentTreeMap(comp, t.blacken(), _count + 1, meta());
}
public PersistentTreeMap assoc(Object key, Object val){
Box found = new Box(null);
Node t = add(tree, key, val, found);
if(t == null) //null == already contains key
{
Node foundNode = (Node) found.val;
if(foundNode.val() == val) //note only get same collection on identity of val, not equals()
return this;
return new PersistentTreeMap(comp, replace(tree, key, val), _count, meta());
}
return new PersistentTreeMap(comp, t.blacken(), _count + 1, meta());
}
public PersistentTreeMap without(Object key){
Box found = new Box(null);
Node t = remove(tree, key, found);
if(t == null)
{
if(found.val == null)//null == doesn't contain key
return this;
//empty
return new PersistentTreeMap(meta(), comp);
}
return new PersistentTreeMap(comp, t.blacken(), _count - 1, meta());
}
public ISeq seq(){
if(_count > 0)
return Seq.create(tree, true, _count);
return null;
}
public IPersistentCollection empty(){
return new PersistentTreeMap(meta(), comp);
}
public ISeq rseq() {
if(_count > 0)
return Seq.create(tree, false, _count);
return null;
}
public Comparator comparator(){
return comp;
}
public Object entryKey(Object entry){
return ((IMapEntry) entry).key();
}
public ISeq seq(boolean ascending){
if(_count > 0)
return Seq.create(tree, ascending, _count);
return null;
}
public ISeq seqFrom(Object key, boolean ascending){
if(_count > 0)
{
ISeq stack = null;
Node t = tree;
while(t != null)
{
int c = doCompare(key, t.key);
if(c == 0)
{
stack = RT.cons(t, stack);
return new Seq(stack, ascending);
}
else if(ascending)
{
if(c < 0)
{
stack = RT.cons(t, stack);
t = t.left();
}
else
t = t.right();
}
else
{
if(c > 0)
{
stack = RT.cons(t, stack);
t = t.right();
}
else
t = t.left();
}
}
if(stack != null)
return new Seq(stack, ascending);
}
return null;
}
public NodeIterator iterator(){
return new NodeIterator(tree, true);
}
public Object kvreduce(IFn f, Object init){
if(tree != null)
init = tree.kvreduce(f,init);
if(RT.isReduced(init))
init = ((IDeref)init).deref();
return init;
}
public NodeIterator reverseIterator(){
return new NodeIterator(tree, false);
}
public Iterator keys(){
return keys(iterator());
}
public Iterator vals(){
return vals(iterator());
}
public Iterator keys(NodeIterator it){
return new KeyIterator(it);
}
public Iterator vals(NodeIterator it){
return new ValIterator(it);
}
public Object minKey(){
Node t = min();
return t != null ? t.key : null;
}
public Node min(){
Node t = tree;
if(t != null)
{
while(t.left() != null)
t = t.left();
}
return t;
}
public Object maxKey(){
Node t = max();
return t != null ? t.key : null;
}
public Node max(){
Node t = tree;
if(t != null)
{
while(t.right() != null)
t = t.right();
}
return t;
}
public int depth(){
return depth(tree);
}
int depth(Node t){
if(t == null)
return 0;
return 1 + Math.max(depth(t.left()), depth(t.right()));
}
public Object valAt(Object key, Object notFound){
Node n = entryAt(key);
return (n != null) ? n.val() : notFound;
}
public Object valAt(Object key){
return valAt(key, null);
}
public int capacity(){
return _count;
}
public int count(){
return _count;
}
public Node entryAt(Object key){
Node t = tree;
while(t != null)
{
int c = doCompare(key, t.key);
if(c == 0)
return t;
else if(c < 0)
t = t.left();
else
t = t.right();
}
return t;
}
public int doCompare(Object k1, Object k2){
// if(comp != null)
return comp.compare(k1, k2);
// return ((Comparable) k1).compareTo(k2);
}
Node add(Node t, Object key, Object val, Box found){
if(t == null)
{
if(val == null)
return new Red(key);
return new RedVal(key, val);
}
int c = doCompare(key, t.key);
if(c == 0)
{
found.val = t;
return null;
}
Node ins = c < 0 ? add(t.left(), key, val, found) : add(t.right(), key, val, found);
if(ins == null) //found below
return null;
if(c < 0)
return t.addLeft(ins);
return t.addRight(ins);
}
Node remove(Node t, Object key, Box found){
if(t == null)
return null; //not found indicator
int c = doCompare(key, t.key);
if(c == 0)
{
found.val = t;
return append(t.left(), t.right());
}
Node del = c < 0 ? remove(t.left(), key, found) : remove(t.right(), key, found);
if(del == null && found.val == null) //not found below
return null;
if(c < 0)
{
if(t.left() instanceof Black)
return balanceLeftDel(t.key, t.val(), del, t.right());
else
return red(t.key, t.val(), del, t.right());
}
if(t.right() instanceof Black)
return balanceRightDel(t.key, t.val(), t.left(), del);
return red(t.key, t.val(), t.left(), del);
// return t.removeLeft(del);
// return t.removeRight(del);
}
static Node append(Node left, Node right){
if(left == null)
return right;
else if(right == null)
return left;
else if(left instanceof Red)
{
if(right instanceof Red)
{
Node app = append(left.right(), right.left());
if(app instanceof Red)
return red(app.key, app.val(),
red(left.key, left.val(), left.left(), app.left()),
red(right.key, right.val(), app.right(), right.right()));
else
return red(left.key, left.val(), left.left(), red(right.key, right.val(), app, right.right()));
}
else
return red(left.key, left.val(), left.left(), append(left.right(), right));
}
else if(right instanceof Red)
return red(right.key, right.val(), append(left, right.left()), right.right());
else //black/black
{
Node app = append(left.right(), right.left());
if(app instanceof Red)
return red(app.key, app.val(),
black(left.key, left.val(), left.left(), app.left()),
black(right.key, right.val(), app.right(), right.right()));
else
return balanceLeftDel(left.key, left.val(), left.left(), black(right.key, right.val(), app, right.right()));
}
}
static Node balanceLeftDel(Object key, Object val, Node del, Node right){
if(del instanceof Red)
return red(key, val, del.blacken(), right);
else if(right instanceof Black)
return rightBalance(key, val, del, right.redden());
else if(right instanceof Red && right.left() instanceof Black)
return red(right.left().key, right.left().val(),
black(key, val, del, right.left().left()),
rightBalance(right.key, right.val(), right.left().right(), right.right().redden()));
else
throw new UnsupportedOperationException("Invariant violation");
}
static Node balanceRightDel(Object key, Object val, Node left, Node del){
if(del instanceof Red)
return red(key, val, left, del.blacken());
else if(left instanceof Black)
return leftBalance(key, val, left.redden(), del);
else if(left instanceof Red && left.right() instanceof Black)
return red(left.right().key, left.right().val(),
leftBalance(left.key, left.val(), left.left().redden(), left.right().left()),
black(key, val, left.right().right(), del));
else
throw new UnsupportedOperationException("Invariant violation");
}
static Node leftBalance(Object key, Object val, Node ins, Node right){
if(ins instanceof Red && ins.left() instanceof Red)
return red(ins.key, ins.val(), ins.left().blacken(), black(key, val, ins.right(), right));
else if(ins instanceof Red && ins.right() instanceof Red)
return red(ins.right().key, ins.right().val(),
black(ins.key, ins.val(), ins.left(), ins.right().left()),
black(key, val, ins.right().right(), right));
else
return black(key, val, ins, right);
}
static Node rightBalance(Object key, Object val, Node left, Node ins){
if(ins instanceof Red && ins.right() instanceof Red)
return red(ins.key, ins.val(), black(key, val, left, ins.left()), ins.right().blacken());
else if(ins instanceof Red && ins.left() instanceof Red)
return red(ins.left().key, ins.left().val(),
black(key, val, left, ins.left().left()),
black(ins.key, ins.val(), ins.left().right(), ins.right()));
else
return black(key, val, left, ins);
}
Node replace(Node t, Object key, Object val){
int c = doCompare(key, t.key);
return t.replace(t.key,
c == 0 ? val : t.val(),
c < 0 ? replace(t.left(), key, val) : t.left(),
c > 0 ? replace(t.right(), key, val) : t.right());
}
PersistentTreeMap(Comparator comp, Node tree, int count, IPersistentMap meta){
this._meta = meta;
this.comp = comp;
this.tree = tree;
this._count = count;
}
static Red red(Object key, Object val, Node left, Node right){
if(left == null && right == null)
{
if(val == null)
return new Red(key);
return new RedVal(key, val);
}
if(val == null)
return new RedBranch(key, left, right);
return new RedBranchVal(key, val, left, right);
}
static Black black(Object key, Object val, Node left, Node right){
if(left == null && right == null)
{
if(val == null)
return new Black(key);
return new BlackVal(key, val);
}
if(val == null)
return new BlackBranch(key, left, right);
return new BlackBranchVal(key, val, left, right);
}
public IPersistentMap meta(){
return _meta;
}
static abstract class Node extends AMapEntry{
final Object key;
Node(Object key){
this.key = key;
}
public Object key(){
return key;
}
public Object val(){
return null;
}
public Object getKey(){
return key();
}
public Object getValue(){
return val();
}
Node left(){
return null;
}
Node right(){
return null;
}
abstract Node addLeft(Node ins);
abstract Node addRight(Node ins);
abstract Node removeLeft(Node del);
abstract Node removeRight(Node del);
abstract Node blacken();
abstract Node redden();
Node balanceLeft(Node parent){
return black(parent.key, parent.val(), this, parent.right());
}
Node balanceRight(Node parent){
return black(parent.key, parent.val(), parent.left(), this);
}
abstract Node replace(Object key, Object val, Node left, Node right);
public Object kvreduce(IFn f, Object init){
if(left() != null){
init = left().kvreduce(f, init);
if(RT.isReduced(init))
return init;
}
init = f.invoke(init, key(), val());
if(RT.isReduced(init))
return init;
if(right() != null){
init = right().kvreduce(f, init);
}
return init;
}
}
static class Black extends Node{
public Black(Object key){
super(key);
}
Node addLeft(Node ins){
return ins.balanceLeft(this);
}
Node addRight(Node ins){
return ins.balanceRight(this);
}
Node removeLeft(Node del){
return balanceLeftDel(key, val(), del, right());
}
Node removeRight(Node del){
return balanceRightDel(key, val(), left(), del);
}
Node blacken(){
return this;
}
Node redden(){
return new Red(key);
}
Node replace(Object key, Object val, Node left, Node right){
return black(key, val, left, right);
}
}
static class BlackVal extends Black{
final Object val;
public BlackVal(Object key, Object val){
super(key);
this.val = val;
}
public Object val(){
return val;
}
Node redden(){
return new RedVal(key, val);
}
}
static class BlackBranch extends Black{
final Node left;
final Node right;
public BlackBranch(Object key, Node left, Node right){
super(key);
this.left = left;
this.right = right;
}
public Node left(){
return left;
}
public Node right(){
return right;
}
Node redden(){
return new RedBranch(key, left, right);
}
}
static class BlackBranchVal extends BlackBranch{
final Object val;
public BlackBranchVal(Object key, Object val, Node left, Node right){
super(key, left, right);
this.val = val;
}
public Object val(){
return val;
}
Node redden(){
return new RedBranchVal(key, val, left, right);
}
}
static class Red extends Node{
public Red(Object key){
super(key);
}
Node addLeft(Node ins){
return red(key, val(), ins, right());
}
Node addRight(Node ins){
return red(key, val(), left(), ins);
}
Node removeLeft(Node del){
return red(key, val(), del, right());
}
Node removeRight(Node del){
return red(key, val(), left(), del);
}
Node blacken(){
return new Black(key);
}
Node redden(){
throw new UnsupportedOperationException("Invariant violation");
}
Node replace(Object key, Object val, Node left, Node right){
return red(key, val, left, right);
}
}
static class RedVal extends Red{
final Object val;
public RedVal(Object key, Object val){
super(key);
this.val = val;
}
public Object val(){
return val;
}
Node blacken(){
return new BlackVal(key, val);
}
}
static class RedBranch extends Red{
final Node left;
final Node right;
public RedBranch(Object key, Node left, Node right){
super(key);
this.left = left;
this.right = right;
}
public Node left(){
return left;
}
public Node right(){
return right;
}
Node balanceLeft(Node parent){
if(left instanceof Red)
return red(key, val(), left.blacken(), black(parent.key, parent.val(), right, parent.right()));
else if(right instanceof Red)
return red(right.key, right.val(), black(key, val(), left, right.left()),
black(parent.key, parent.val(), right.right(), parent.right()));
else
return super.balanceLeft(parent);
}
Node balanceRight(Node parent){
if(right instanceof Red)
return red(key, val(), black(parent.key, parent.val(), parent.left(), left), right.blacken());
else if(left instanceof Red)
return red(left.key, left.val(), black(parent.key, parent.val(), parent.left(), left.left()),
black(key, val(), left.right(), right));
else
return super.balanceRight(parent);
}
Node blacken(){
return new BlackBranch(key, left, right);
}
}
static class RedBranchVal extends RedBranch{
final Object val;
public RedBranchVal(Object key, Object val, Node left, Node right){
super(key, left, right);
this.val = val;
}
public Object val(){
return val;
}
Node blacken(){
return new BlackBranchVal(key, val, left, right);
}
}
static public class Seq extends ASeq{
final ISeq stack;
final boolean asc;
final int cnt;
public Seq(ISeq stack, boolean asc){
this.stack = stack;
this.asc = asc;
this.cnt = -1;
}
public Seq(ISeq stack, boolean asc, int cnt){
this.stack = stack;
this.asc = asc;
this.cnt = cnt;
}
Seq(IPersistentMap meta, ISeq stack, boolean asc, int cnt){
super(meta);
this.stack = stack;
this.asc = asc;
this.cnt = cnt;
}
static Seq create(Node t, boolean asc, int cnt){
return new Seq(push(t, null, asc), asc, cnt);
}
static ISeq push(Node t, ISeq stack, boolean asc){
while(t != null)
{
stack = RT.cons(t, stack);
t = asc ? t.left() : t.right();
}
return stack;
}
public Object first(){
return stack.first();
}
public ISeq next(){
Node t = (Node) stack.first();
ISeq nextstack = push(asc ? t.right() : t.left(), stack.next(), asc);
if(nextstack != null)
{
return new Seq(nextstack, asc, cnt - 1);
}
return null;
}
public int count(){
if(cnt < 0)
return super.count();
return cnt;
}
public Obj withMeta(IPersistentMap meta){
return new Seq(meta, stack, asc, cnt);
}
}
static public class NodeIterator implements Iterator{
Stack stack = new Stack();
boolean asc;
NodeIterator(Node t, boolean asc){
this.asc = asc;
push(t);
}
void push(Node t){
while(t != null)
{
stack.push(t);
t = asc ? t.left() : t.right();
}
}
public boolean hasNext(){
return !stack.isEmpty();
}
public Object next(){
Node t = (Node) stack.pop();
push(asc ? t.right() : t.left());
return t;
}
public void remove(){
throw new UnsupportedOperationException();
}
}
static class KeyIterator implements Iterator{
NodeIterator it;
KeyIterator(NodeIterator it){
this.it = it;
}
public boolean hasNext(){
return it.hasNext();
}
public Object next(){
return ((Node) it.next()).key;
}
public void remove(){
throw new UnsupportedOperationException();
}
}
static class ValIterator implements Iterator{
NodeIterator it;
ValIterator(NodeIterator it){
this.it = it;
}
public boolean hasNext(){
return it.hasNext();
}
public Object next(){
return ((Node) it.next()).val();
}
public void remove(){
throw new UnsupportedOperationException();
}
}
/*
static public void main(String args[]){
if(args.length != 1)
System.err.println("Usage: RBTree n");
int n = Integer.parseInt(args[0]);
Integer[] ints = new Integer[n];
for(int i = 0; i < ints.length; i++)
{
ints[i] = i;
}
Collections.shuffle(Arrays.asList(ints));
//force the ListMap class loading now
// try
// {
//
// //PersistentListMap.EMPTY.assocEx(1, null).assocEx(2,null).assocEx(3,null);
// }
// catch(Exception e)
// {
// e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
// }
System.out.println("Building set");
//IPersistentMap set = new PersistentArrayMap();
//IPersistentMap set = new PersistentHashtableMap(1001);
IPersistentMap set = PersistentHashMap.EMPTY;
//IPersistentMap set = new ListMap();
//IPersistentMap set = new ArrayMap();
//IPersistentMap set = new PersistentTreeMap();
// for(int i = 0; i < ints.length; i++)
// {
// Integer anInt = ints[i];
// set = set.add(anInt);
// }
long startTime = System.nanoTime();
for(Integer anInt : ints)
{
set = set.assoc(anInt, anInt);
}
//System.out.println("_count = " + set.count());
// System.out.println("_count = " + set._count + ", min: " + set.minKey() + ", max: " + set.maxKey()
// + ", depth: " + set.depth());
for(Object aSet : set)
{
IMapEntry o = (IMapEntry) aSet;
if(!set.contains(o.key()))
System.err.println("Can't find: " + o.key());
//else if(n < 2000)
// System.out.print(o.key().toString() + ",");
}
Random rand = new Random(42);
for(int i = 0; i < ints.length / 2; i++)
{
Integer anInt = ints[rand.nextInt(n)];
set = set.without(anInt);
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println();
System.out.println("_count = " + set.count() + ", time: " + estimatedTime / 1000000);
System.out.println("Building ht");
Hashtable ht = new Hashtable(1001);
startTime = System.nanoTime();
// for(int i = 0; i < ints.length; i++)
// {
// Integer anInt = ints[i];
// ht.put(anInt,null);
// }
for(Integer anInt : ints)
{
ht.put(anInt, anInt);
}
//System.out.println("size = " + ht.size());
//Iterator it = ht.entrySet().iterator();
for(Object o1 : ht.entrySet())
{
Map.Entry o = (Map.Entry) o1;
if(!ht.containsKey(o.getKey()))
System.err.println("Can't find: " + o);
//else if(n < 2000)
// System.out.print(o.toString() + ",");
}
rand = new Random(42);
for(int i = 0; i < ints.length / 2; i++)
{
Integer anInt = ints[rand.nextInt(n)];
ht.remove(anInt);
}
estimatedTime = System.nanoTime() - startTime;
System.out.println();
System.out.println("size = " + ht.size() + ", time: " + estimatedTime / 1000000);
System.out.println("set lookup");
startTime = System.nanoTime();
int c = 0;
for(Integer anInt : ints)
{
if(!set.contains(anInt))
++c;
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
System.out.println("ht lookup");
startTime = System.nanoTime();
c = 0;
for(Integer anInt : ints)
{
if(!ht.containsKey(anInt))
++c;
}
estimatedTime = System.nanoTime() - startTime;
System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
// System.out.println("_count = " + set._count + ", min: " + set.minKey() + ", max: " + set.maxKey()
// + ", depth: " + set.depth());
}
*/
}
================================================
FILE: src/jvm/clojure/lang/PersistentTreeSet.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
import java.util.Comparator;
public class PersistentTreeSet extends APersistentSet implements IObj, Reversible, Sorted{
static public final PersistentTreeSet EMPTY = new PersistentTreeSet(null, PersistentTreeMap.EMPTY);
final IPersistentMap _meta;
static public PersistentTreeSet create(ISeq items){
PersistentTreeSet ret = EMPTY;
for(; items != null; items = items.next())
{
ret = (PersistentTreeSet) ret.cons(items.first());
}
return ret;
}
static public PersistentTreeSet create(Comparator comp, ISeq items){
PersistentTreeSet ret = new PersistentTreeSet(null, new PersistentTreeMap(null, comp));
for(; items != null; items = items.next())
{
ret = (PersistentTreeSet) ret.cons(items.first());
}
return ret;
}
PersistentTreeSet(IPersistentMap meta, IPersistentMap impl){
super(impl);
this._meta = meta;
}
public IPersistentSet disjoin(Object key) {
if(contains(key))
return new PersistentTreeSet(meta(),impl.without(key));
return this;
}
public IPersistentSet cons(Object o){
if(contains(o))
return this;
return new PersistentTreeSet(meta(),impl.assoc(o,o));
}
public IPersistentCollection empty(){
return new PersistentTreeSet(meta(),(PersistentTreeMap)impl.empty());
}
public ISeq rseq() {
return APersistentMap.KeySeq.create(((Reversible) impl).rseq());
}
public PersistentTreeSet withMeta(IPersistentMap meta){
return new PersistentTreeSet(meta, impl);
}
public Comparator comparator(){
return ((Sorted)impl).comparator();
}
public Object entryKey(Object entry){
return entry;
}
public ISeq seq(boolean ascending){
PersistentTreeMap m = (PersistentTreeMap) impl;
return RT.keys(m.seq(ascending));
}
public ISeq seqFrom(Object key, boolean ascending){
PersistentTreeMap m = (PersistentTreeMap) impl;
return RT.keys(m.seqFrom(key,ascending));
}
public IPersistentMap meta(){
return _meta;
}
}
================================================
FILE: src/jvm/clojure/lang/PersistentVector.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 5, 2007 */
package clojure.lang;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce{
public static class Node implements Serializable {
transient public final AtomicReference edit;
public final Object[] array;
public Node(AtomicReference edit, Object[] array){
this.edit = edit;
this.array = array;
}
Node(AtomicReference edit){
this.edit = edit;
this.array = new Object[32];
}
}
final static AtomicReference NOEDIT = new AtomicReference(null);
public final static Node EMPTY_NODE = new Node(NOEDIT, new Object[32]);
final int cnt;
public final int shift;
public final Node root;
public final Object[] tail;
final IPersistentMap _meta;
public final static PersistentVector EMPTY = new PersistentVector(0, 5, EMPTY_NODE, new Object[]{});
private static final IFn TRANSIENT_VECTOR_CONJ = new AFn() {
public Object invoke(Object coll, Object val) {
return ((ITransientVector)coll).conj(val);
}
public Object invoke(Object coll) {
return coll;
}
};
static public PersistentVector create(IReduceInit items) {
TransientVector ret = EMPTY.asTransient();
items.reduce(TRANSIENT_VECTOR_CONJ, ret);
return ret.persistent();
}
static public PersistentVector create(ISeq items){
Object[] arr = new Object[32];
int i = 0;
for(;items != null && i < 32; items = items.next())
arr[i++] = items.first();
if(items != null) { // >32, construct with array directly
PersistentVector start = new PersistentVector(32, 5, EMPTY_NODE, arr);
TransientVector ret = start.asTransient();
for (; items != null; items = items.next())
ret = ret.uncheckedConj(items.first());
return ret.persistent();
} else if(i == 32) { // exactly 32, skip copy
return new PersistentVector(32, 5, EMPTY_NODE, arr);
} else { // <32, copy to minimum array and construct
Object[] arr2 = new Object[i];
System.arraycopy(arr, 0, arr2, 0, i);
return new PersistentVector(i, 5, EMPTY_NODE, arr2);
}
}
static public PersistentVector create(List list){
int size = list.size();
if (size <= 32)
return new PersistentVector(size, 5, PersistentVector.EMPTY_NODE, list.toArray());
TransientVector ret = EMPTY.asTransient();
for(int i=0; i>> 5) << 5;
}
public Object[] arrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = (Node) node.array[(i >>> level) & 0x01f];
return node.array;
}
throw new IndexOutOfBoundsException();
}
public Object nth(int i){
Object[] node = arrayFor(i);
return node[i & 0x01f];
}
public Object nth(int i, Object notFound){
if(i >= 0 && i < cnt)
return nth(i);
return notFound;
}
public PersistentVector assocN(int i, Object val){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
{
Object[] newTail = new Object[tail.length];
System.arraycopy(tail, 0, newTail, 0, tail.length);
newTail[i & 0x01f] = val;
return new PersistentVector(meta(), cnt, shift, root, newTail);
}
return new PersistentVector(meta(), cnt, shift, doAssoc(shift, root, i, val), tail);
}
if(i == cnt)
return cons(val);
throw new IndexOutOfBoundsException();
}
private static Node doAssoc(int level, Node node, int i, Object val){
Node ret = new Node(node.edit,node.array.clone());
if(level == 0)
{
ret.array[i & 0x01f] = val;
}
else
{
int subidx = (i >>> level) & 0x01f;
ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);
}
return ret;
}
public int count(){
return cnt;
}
public PersistentVector withMeta(IPersistentMap meta){
return new PersistentVector(meta, cnt, shift, root, tail);
}
public IPersistentMap meta(){
return _meta;
}
public PersistentVector cons(Object val){
int i = cnt;
//room in tail?
// if(tail.length < 32)
if(cnt - tailoff() < 32)
{
Object[] newTail = new Object[tail.length + 1];
System.arraycopy(tail, 0, newTail, 0, tail.length);
newTail[tail.length] = val;
return new PersistentVector(meta(), cnt + 1, shift, root, newTail);
}
//full tail, push into tree
Node newroot;
Node tailnode = new Node(root.edit,tail);
int newshift = shift;
//overflow root?
if((cnt >>> 5) > (1 << shift))
{
newroot = new Node(root.edit);
newroot.array[0] = root;
newroot.array[1] = newPath(root.edit,shift, tailnode);
newshift += 5;
}
else
newroot = pushTail(shift, root, tailnode);
return new PersistentVector(meta(), cnt + 1, newshift, newroot, new Object[]{val});
}
private Node pushTail(int level, Node parent, Node tailnode){
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in copy of parent
int subidx = ((cnt - 1) >>> level) & 0x01f;
Node ret = new Node(parent.edit, parent.array.clone());
Node nodeToInsert;
if(level == 5)
{
nodeToInsert = tailnode;
}
else
{
Node child = (Node) parent.array[subidx];
nodeToInsert = (child != null)?
pushTail(level-5,child, tailnode)
:newPath(root.edit,level-5, tailnode);
}
ret.array[subidx] = nodeToInsert;
return ret;
}
private static Node newPath(AtomicReference edit,int level, Node node){
if(level == 0)
return node;
Node ret = new Node(edit);
ret.array[0] = newPath(edit, level - 5, node);
return ret;
}
public IChunkedSeq chunkedSeq(){
if(count() == 0)
return null;
return new ChunkedSeq(this,0,0);
}
public ISeq seq(){
return chunkedSeq();
}
@Override
Iterator rangedIterator(final int start, final int end){
return new Iterator(){
int i = start;
int base = i - (i%32);
Object[] array = (start < count())?arrayFor(i):null;
public boolean hasNext(){
return i < end;
}
public Object next(){
if(i-base == 32){
array = arrayFor(i);
base += 32;
}
return array[i++ & 0x01f];
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public Iterator iterator(){return rangedIterator(0,count());}
public Object reduce(IFn f){
Object init;
if (cnt > 0)
init = arrayFor(0)[0];
else
return f.invoke();
int step = 0;
for(int i=0;i 1)
if(cnt-tailoff() > 1)
{
Object[] newTail = new Object[tail.length - 1];
System.arraycopy(tail, 0, newTail, 0, newTail.length);
return new PersistentVector(meta(), cnt - 1, shift, root, newTail);
}
Object[] newtail = arrayFor(cnt - 2);
Node newroot = popTail(shift, root);
int newshift = shift;
if(newroot == null)
{
newroot = EMPTY_NODE;
}
if(shift > 5 && newroot.array[1] == null)
{
newroot = (Node) newroot.array[0];
newshift -= 5;
}
return new PersistentVector(meta(), cnt - 1, newshift, newroot, newtail);
}
private Node popTail(int level, Node node){
int subidx = ((cnt-2) >>> level) & 0x01f;
if(level > 5)
{
Node newchild = popTail(level - 5, (Node) node.array[subidx]);
if(newchild == null && subidx == 0)
return null;
else
{
Node ret = new Node(root.edit, node.array.clone());
ret.array[subidx] = newchild;
return ret;
}
}
else if(subidx == 0)
return null;
else
{
Node ret = new Node(root.edit, node.array.clone());
ret.array[subidx] = null;
return ret;
}
}
static final class TransientVector extends AFn implements ITransientVector, Counted{
volatile int cnt;
volatile int shift;
volatile Node root;
volatile Object[] tail;
TransientVector(int cnt, int shift, Node root, Object[] tail){
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
TransientVector(PersistentVector v){
this(v.cnt, v.shift, editableRoot(v.root), editableTail(v.tail));
}
public int count(){
ensureEditable();
return cnt;
}
Node ensureEditable(Node node){
if(node.edit == root.edit)
return node;
return new Node(root.edit, node.array.clone());
}
void ensureEditable(){
if(root.edit.get() == null)
throw new IllegalAccessError("Transient used after persistent! call");
// root = editableRoot(root);
// tail = editableTail(tail);
}
static Node editableRoot(Node node){
return new Node(new AtomicReference(Thread.currentThread()), node.array.clone());
}
public PersistentVector persistent(){
ensureEditable();
// Thread owner = root.edit.get();
// if(owner != null && owner != Thread.currentThread())
// {
// throw new IllegalAccessError("Mutation release by non-owner thread");
// }
root.edit.set(null);
Object[] trimmedTail = new Object[cnt-tailoff()];
System.arraycopy(tail,0,trimmedTail,0,trimmedTail.length);
return new PersistentVector(cnt, shift, root, trimmedTail);
}
static Object[] editableTail(Object[] tl){
Object[] ret = new Object[32];
System.arraycopy(tl,0,ret,0,tl.length);
return ret;
}
public TransientVector conj(Object val){
ensureEditable();
return uncheckedConj(val);
}
protected TransientVector uncheckedConj(Object val) {
int i = cnt;
//room in tail?
if(i - tailoff() < 32)
{
tail[i & 0x01f] = val;
++cnt;
return this;
}
//full tail, push into tree
Node newroot;
Node tailnode = new Node(root.edit, tail);
tail = new Object[32];
tail[0] = val;
int newshift = shift;
//overflow root?
if((cnt >>> 5) > (1 << shift))
{
newroot = new Node(root.edit);
newroot.array[0] = root;
newroot.array[1] = newPath(root.edit,shift, tailnode);
newshift += 5;
}
else
newroot = pushTail(shift, root, tailnode);
root = newroot;
shift = newshift;
++cnt;
return this;
}
private Node pushTail(int level, Node parent, Node tailnode){
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in parent
parent = ensureEditable(parent);
int subidx = ((cnt - 1) >>> level) & 0x01f;
Node ret = parent;
Node nodeToInsert;
if(level == 5)
{
nodeToInsert = tailnode;
}
else
{
Node child = (Node) parent.array[subidx];
nodeToInsert = (child != null) ?
pushTail(level - 5, child, tailnode)
: newPath(root.edit, level - 5, tailnode);
}
ret.array[subidx] = nodeToInsert;
return ret;
}
final private int tailoff(){
if(cnt < 32)
return 0;
return ((cnt-1) >>> 5) << 5;
}
private Object[] arrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = (Node) node.array[(i >>> level) & 0x01f];
return node.array;
}
throw new IndexOutOfBoundsException();
}
private Object[] editableArrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = ensureEditable((Node) node.array[(i >>> level) & 0x01f]);
return node.array;
}
throw new IndexOutOfBoundsException();
}
public Object valAt(Object key){
//note - relies on ensureEditable in 2-arg valAt
return valAt(key, null);
}
public Object valAt(Object key, Object notFound){
ensureEditable();
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < cnt)
return nth(i);
}
return notFound;
}
public Object invoke(Object arg1) {
//note - relies on ensureEditable in nth
if(Util.isInteger(arg1))
return nth(((Number) arg1).intValue());
throw new IllegalArgumentException("Key must be integer");
}
public Object nth(int i){
ensureEditable();
Object[] node = arrayFor(i);
return node[i & 0x01f];
}
public Object nth(int i, Object notFound){
if(i >= 0 && i < count())
return nth(i);
return notFound;
}
public TransientVector assocN(int i, Object val){
ensureEditable();
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
{
tail[i & 0x01f] = val;
return this;
}
root = doAssoc(shift, root, i, val);
return this;
}
if(i == cnt)
return conj(val);
throw new IndexOutOfBoundsException();
}
public TransientVector assoc(Object key, Object val){
//note - relies on ensureEditable in assocN
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
return assocN(i, val);
}
throw new IllegalArgumentException("Key must be integer");
}
private Node doAssoc(int level, Node node, int i, Object val){
node = ensureEditable(node);
Node ret = node;
if(level == 0)
{
ret.array[i & 0x01f] = val;
}
else
{
int subidx = (i >>> level) & 0x01f;
ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);
}
return ret;
}
public TransientVector pop(){
ensureEditable();
if(cnt == 0)
throw new IllegalStateException("Can't pop empty vector");
if(cnt == 1)
{
cnt = 0;
return this;
}
int i = cnt - 1;
//pop in tail?
if((i & 0x01f) > 0)
{
--cnt;
return this;
}
Object[] newtail = editableArrayFor(cnt - 2);
Node newroot = popTail(shift, root);
int newshift = shift;
if(newroot == null)
{
newroot = new Node(root.edit);
}
if(shift > 5 && newroot.array[1] == null)
{
newroot = ensureEditable((Node) newroot.array[0]);
newshift -= 5;
}
root = newroot;
shift = newshift;
--cnt;
tail = newtail;
return this;
}
private Node popTail(int level, Node node){
node = ensureEditable(node);
int subidx = ((cnt - 2) >>> level) & 0x01f;
if(level > 5)
{
Node newchild = popTail(level - 5, (Node) node.array[subidx]);
if(newchild == null && subidx == 0)
return null;
else
{
Node ret = node;
ret.array[subidx] = newchild;
return ret;
}
}
else if(subidx == 0)
return null;
else
{
Node ret = node;
ret.array[subidx] = null;
return ret;
}
}
}
/*
static public void main(String[] args){
if(args.length != 3)
{
System.err.println("Usage: PersistentVector size writes reads");
return;
}
int size = Integer.parseInt(args[0]);
int writes = Integer.parseInt(args[1]);
int reads = Integer.parseInt(args[2]);
// Vector v = new Vector(size);
ArrayList v = new ArrayList(size);
// v.setSize(size);
//PersistentArray p = new PersistentArray(size);
PersistentVector p = PersistentVector.EMPTY;
// MutableVector mp = p.mutable();
for(int i = 0; i < size; i++)
{
v.add(i);
// v.set(i, i);
//p = p.set(i, 0);
p = p.cons(i);
// mp = mp.conj(i);
}
Random rand;
rand = new Random(42);
long tv = 0;
System.out.println("ArrayList");
long startTime = System.nanoTime();
for(int i = 0; i < writes; i++)
{
v.set(rand.nextInt(size), i);
}
for(int i = 0; i < reads; i++)
{
tv += (Integer) v.get(rand.nextInt(size));
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
System.out.println("PersistentVector");
rand = new Random(42);
startTime = System.nanoTime();
long tp = 0;
// PersistentVector oldp = p;
//Random rand2 = new Random(42);
MutableVector mp = p.mutable();
for(int i = 0; i < writes; i++)
{
// p = p.assocN(rand.nextInt(size), i);
mp = mp.assocN(rand.nextInt(size), i);
// mp = mp.assoc(rand.nextInt(size), i);
//dummy set to force perverse branching
//oldp = oldp.assocN(rand2.nextInt(size), i);
}
for(int i = 0; i < reads; i++)
{
// tp += (Integer) p.nth(rand.nextInt(size));
tp += (Integer) mp.nth(rand.nextInt(size));
}
// p = mp.immutable();
//mp.cons(42);
estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
for(int i = 0; i < size / 2; i++)
{
mp = mp.pop();
// p = p.pop();
v.remove(v.size() - 1);
}
p = (PersistentVector) mp.immutable();
//mp.pop(); //should fail
for(int i = 0; i < size / 2; i++)
{
tp += (Integer) p.nth(i);
tv += (Integer) v.get(i);
}
System.out.println("Done: " + tv + ", " + tp);
}
// */
}
================================================
FILE: src/jvm/clojure/lang/ProxyHandler.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Oct 4, 2007 */
package clojure.lang;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler{
//method-name-string->fn
final IPersistentMap fns;
public ProxyHandler(IPersistentMap fns){
this.fns = fns;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
Class rt = method.getReturnType();
IFn fn = (IFn) fns.valAt(method.getName());
if(fn == null)
{
if(rt == Void.TYPE)
return null;
else if(method.getName().equals("equals"))
{
return proxy == args[0];
}
else if(method.getName().equals("hashCode"))
{
return System.identityHashCode(proxy);
}
else if(method.getName().equals("toString"))
{
return "Proxy: " + System.identityHashCode(proxy);
}
throw new UnsupportedOperationException();
}
Object ret = fn.applyTo(ArraySeq.create(args));
if(rt == Void.TYPE)
return null;
else if(rt.isPrimitive())
{
if(rt == Character.TYPE)
return ret;
else if(rt == Integer.TYPE)
return ((Number) ret).intValue();
else if(rt == Long.TYPE)
return ((Number) ret).longValue();
else if(rt == Float.TYPE)
return ((Number) ret).floatValue();
else if(rt == Double.TYPE)
return ((Number) ret).doubleValue();
else if(rt == Boolean.TYPE && !(ret instanceof Boolean))
return ret == null ? Boolean.FALSE : Boolean.TRUE;
else if(rt == Byte.TYPE)
return (byte) ((Number) ret).intValue();
else if(rt == Short.TYPE)
return (short) ((Number) ret).intValue();
}
return ret;
}
}
================================================
FILE: src/jvm/clojure/lang/RT.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 4:28:27 PM */
package clojure.lang;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectStreamException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*-[
#include "clojure/core_ns.h"
]-*/
public class RT {
static final public Boolean T = Boolean.TRUE;// Keyword.intern(Symbol.intern(null,
// "t"));
static final public Boolean F = Boolean.FALSE;// Keyword.intern(Symbol.intern(null,
// "t"));
static final public String LOADER_SUFFIX = "__init";
static AtomicInteger id = new AtomicInteger(1);
// simple-symbol->class
final static IPersistentMap DEFAULT_IMPORTS = ObjC.objc ? map() : map(
// Symbol.intern("RT"), "clojure.lang.RT",
// Symbol.intern("Num"), "clojure.lang.Num",
// Symbol.intern("Symbol"), "clojure.lang.Symbol",
// Symbol.intern("Keyword"), "clojure.lang.Keyword",
// Symbol.intern("Var"), "clojure.lang.Var",
// Symbol.intern("Ref"), "clojure.lang.Ref",
// Symbol.intern("IFn"), "clojure.lang.IFn",
// Symbol.intern("IObj"), "clojure.lang.IObj",
// Symbol.intern("ISeq"), "clojure.lang.ISeq",
// Symbol.intern("IPersistentCollection"),
// "clojure.lang.IPersistentCollection",
// Symbol.intern("IPersistentMap"), "clojure.lang.IPersistentMap",
// Symbol.intern("IPersistentList"), "clojure.lang.IPersistentList",
// Symbol.intern("IPersistentVector"), "clojure.lang.IPersistentVector",
Symbol.intern("Boolean"),
Boolean.class,
Symbol.intern("Byte"),
Byte.class,
Symbol.intern("Character"),
Character.class,
Symbol.intern("Class"),
Class.class,
Symbol.intern("ClassLoader"),
ClassLoader.class,
// Symbol.intern("Compiler"), Compiler.class,
Symbol.intern("Double"), Double.class, Symbol.intern("Enum"), Enum.class,
Symbol.intern("Float"), Float.class, Symbol.intern("Integer"),
Integer.class, Symbol.intern("Long"), Long.class, Symbol.intern("Math"),
Math.class, Symbol.intern("Number"), Number.class,
Symbol.intern("Object"), Object.class, Symbol.intern("Package"),
Package.class, Symbol.intern("Runtime"), Runtime.class,
Symbol.intern("SecurityManager"), SecurityManager.class,
Symbol.intern("Short"), Short.class, Symbol.intern("StackTraceElement"),
StackTraceElement.class, Symbol.intern("StrictMath"), StrictMath.class,
Symbol.intern("String"), String.class, Symbol.intern("StringBuffer"),
StringBuffer.class, Symbol.intern("StringBuilder"), StringBuilder.class,
Symbol.intern("System"), System.class, Symbol.intern("Thread"),
Thread.class, Symbol.intern("ThreadGroup"), ThreadGroup.class,
Symbol.intern("ThreadLocal"), ThreadLocal.class,
Symbol.intern("Throwable"), Throwable.class, Symbol.intern("Void"),
Void.class, Symbol.intern("Appendable"), Appendable.class,
Symbol.intern("CharSequence"), CharSequence.class,
Symbol.intern("Cloneable"), Cloneable.class, Symbol.intern("Comparable"),
Comparable.class, Symbol.intern("Iterable"), Iterable.class,
Symbol.intern("Readable"), Readable.class, Symbol.intern("Runnable"),
Runnable.class, Symbol.intern("Callable"), Callable.class,
Symbol.intern("BigInteger"), BigInteger.class,
Symbol.intern("BigDecimal"), BigDecimal.class,
Symbol.intern("ArithmeticException"), ArithmeticException.class,
Symbol.intern("ArrayIndexOutOfBoundsException"),
ArrayIndexOutOfBoundsException.class,Symbol.intern("NoClassDefFoundError"),
NoClassDefFoundError.class,
Symbol.intern("ArrayStoreException"), ArrayStoreException.class,
Symbol.intern("ClassCastException"), ClassCastException.class,
Symbol.intern("ClassNotFoundException"), ClassNotFoundException.class,
Symbol.intern("CloneNotSupportedException"),
CloneNotSupportedException.class, Symbol.intern("Exception"),
Exception.class, Symbol.intern("IllegalAccessException"),
IllegalAccessException.class, Symbol.intern("IllegalArgumentException"),
IllegalArgumentException.class,
Symbol.intern("IllegalMonitorStateException"),
IllegalMonitorStateException.class,
Symbol.intern("IllegalStateException"), IllegalStateException.class,
Symbol.intern("IllegalThreadStateException"),
IllegalThreadStateException.class,
Symbol.intern("IndexOutOfBoundsException"),
IndexOutOfBoundsException.class, Symbol.intern("InstantiationException"),
InstantiationException.class, Symbol.intern("InterruptedException"),
InterruptedException.class, Symbol.intern("NegativeArraySizeException"),
NegativeArraySizeException.class, Symbol.intern("NoSuchFieldException"),
NoSuchFieldException.class, Symbol.intern("NoSuchMethodException"),
NoSuchMethodException.class, Symbol.intern("NullPointerException"),
NullPointerException.class, Symbol.intern("NumberFormatException"),
NumberFormatException.class, Symbol.intern("RuntimeException"),
RuntimeException.class, Symbol.intern("SecurityException"),
SecurityException.class,
Symbol.intern("StringIndexOutOfBoundsException"),
StringIndexOutOfBoundsException.class,
Symbol.intern("TypeNotPresentException"), TypeNotPresentException.class,
Symbol.intern("UnsupportedOperationException"),
UnsupportedOperationException.class,
Symbol.intern("AbstractMethodError"), AbstractMethodError.class,
Symbol.intern("AssertionError"), AssertionError.class,
Symbol.intern("ClassFormatError"), ClassFormatError.class,
Symbol.intern("Error"), Error.class,
Symbol.intern("ExceptionInInitializerError"),
ExceptionInInitializerError.class, Symbol.intern("IllegalAccessError"),
IllegalAccessError.class, Symbol.intern("IncompatibleClassChangeError"),
IncompatibleClassChangeError.class, Symbol.intern("InternalError"),
InternalError.class, Symbol.intern("LinkageError"), LinkageError.class,
Symbol.intern("NoSuchFieldError"), NoSuchFieldError.class,
Symbol.intern("NoSuchMethodError"), NoSuchMethodError.class,
Symbol.intern("OutOfMemoryError"), OutOfMemoryError.class,
Symbol.intern("ThreadDeath"), ThreadDeath.class,
Symbol.intern("VirtualMachineError"), VirtualMachineError.class,
Symbol.intern("Thread$UncaughtExceptionHandler"),
Thread.UncaughtExceptionHandler.class, Symbol.intern("Thread$State"),
Thread.State.class, Symbol.intern("Deprecated"), Deprecated.class,
Symbol.intern("Override"), Override.class,
Symbol.intern("SuppressWarnings"), SuppressWarnings.class
// Symbol.intern("Collection"), "java.util.Collection",
// Symbol.intern("Comparator"), "java.util.Comparator",
// Symbol.intern("Enumeration"), "java.util.Enumeration",
// Symbol.intern("EventListener"), "java.util.EventListener",
// Symbol.intern("Formattable"), "java.util.Formattable",
// Symbol.intern("Iterator"), "java.util.Iterator",
// Symbol.intern("List"), "java.util.List",
// Symbol.intern("ListIterator"), "java.util.ListIterator",
// Symbol.intern("Map"), "java.util.Map",
// Symbol.intern("Map$Entry"), "java.util.Map$Entry",
// Symbol.intern("Observer"), "java.util.Observer",
// Symbol.intern("Queue"), "java.util.Queue",
// Symbol.intern("RandomAccess"), "java.util.RandomAccess",
// Symbol.intern("Set"), "java.util.Set",
// Symbol.intern("SortedMap"), "java.util.SortedMap",
// Symbol.intern("SortedSet"), "java.util.SortedSet"
);
// single instance of UTF-8 Charset, so as to avoid catching
// UnsupportedCharsetExceptions everywhere
static public Charset UTF8 = Charset.forName("UTF-8");
static Object readTrueFalseUnknown(String s) {
if (s.equals("true"))
return Boolean.TRUE;
else if (s.equals("false"))
return Boolean.FALSE;
return Keyword.intern(null, "unknown");
}
static public final Namespace CLOJURE_NS = Namespace.findOrCreate(Symbol
.intern("clojure.core"));
// static final Namespace USER_NS =
// Namespace.findOrCreate(Symbol.intern("user"));
final static public Var OUT = Var.intern(CLOJURE_NS, Symbol.intern("*out*"),
new OutputStreamWriter(System.out)).setDynamic();
final static public Var IN = Var.intern(CLOJURE_NS, Symbol.intern("*in*"),
new LineNumberingPushbackReader(new InputStreamReader(System.in)))
.setDynamic();
final static public Var ERR = Var.intern(CLOJURE_NS, Symbol.intern("*err*"),
new PrintWriter(new OutputStreamWriter(System.err), true)).setDynamic();
final static Keyword TAG_KEY = Keyword.intern(null, "tag");
final static Keyword CONST_KEY = Keyword.intern(null, "const");
final static public Var AGENT = Var.intern(CLOJURE_NS,
Symbol.intern("*agent*"), null).setDynamic();
static Object readeval = readTrueFalseUnknown(System.getProperty(
"clojure.read.eval", "true"));
final static public Var READEVAL = Var.intern(CLOJURE_NS,
Symbol.intern("*read-eval*"), readeval).setDynamic();
final static public Var DATA_READERS = Var.intern(CLOJURE_NS,
Symbol.intern("*data-readers*"), RT.map()).setDynamic();
final static public Var DEFAULT_DATA_READER_FN = Var.intern(CLOJURE_NS,
Symbol.intern("*default-data-reader-fn*"), RT.map()).setDynamic();
final static public Var DEFAULT_DATA_READERS = Var.intern(CLOJURE_NS,
Symbol.intern("default-data-readers"), RT.map());
final static public Var SUPPRESS_READ = Var.intern(CLOJURE_NS, Symbol.intern("*suppress-read*"), null).setDynamic();
final static public Var ASSERT = Var.intern(CLOJURE_NS,
Symbol.intern("*assert*"), T).setDynamic();
final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS,
Symbol.intern("*math-context*"), null).setDynamic();
static Keyword LINE_KEY = Keyword.intern(null, "line");
static Keyword COLUMN_KEY = Keyword.intern(null, "column");
static Keyword FILE_KEY = Keyword.intern(null, "file");
static Keyword DECLARED_KEY = Keyword.intern(null, "declared");
static Keyword DOC_KEY = Keyword.intern(null, "doc");
final static public Var USE_CONTEXT_CLASSLOADER = Var.intern(CLOJURE_NS,
Symbol.intern("*use-context-classloader*"), T).setDynamic();
// boolean
static final public Var UNCHECKED_MATH = Var.intern(
Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*unchecked-math*"), Boolean.FALSE).setDynamic();
// final static public Var CURRENT_MODULE =
// Var.intern(Symbol.intern("clojure.core", "current-module"),
// Module.findOrCreateModule("clojure/user"));
final static Symbol LOAD_FILE = Symbol.intern("load-file");
final static Symbol IN_NAMESPACE = Symbol.intern("in-ns");
final static Symbol NAMESPACE = Symbol.intern("ns");
static final Symbol IDENTICAL = Symbol.intern("identical?");
final static Var CMD_LINE_ARGS = Var.intern(CLOJURE_NS,
Symbol.intern("*command-line-args*"), null).setDynamic();
// symbol
final public static Var CURRENT_NS = Var.intern(CLOJURE_NS,
Symbol.intern("*ns*"), CLOJURE_NS).setDynamic();
final static Var FLUSH_ON_NEWLINE = Var.intern(CLOJURE_NS,
Symbol.intern("*flush-on-newline*"), T).setDynamic();
final static Var PRINT_META = Var.intern(CLOJURE_NS,
Symbol.intern("*print-meta*"), F).setDynamic();
final static Var PRINT_READABLY = Var.intern(CLOJURE_NS,
Symbol.intern("*print-readably*"), T).setDynamic();
final static Var PRINT_DUP = Var.intern(CLOJURE_NS,
Symbol.intern("*print-dup*"), F).setDynamic();
public final static Var WARN_ON_REFLECTION = Var.intern(CLOJURE_NS,
Symbol.intern("*warn-on-reflection*"), F).setDynamic();
final static Var ALLOW_UNRESOLVED_VARS = Var.intern(CLOJURE_NS,
Symbol.intern("*allow-unresolved-vars*"), F).setDynamic();
final static Var IN_NS_VAR = Var
.intern(CLOJURE_NS, Symbol.intern("in-ns"), F);
final static Var NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("ns"), F);
final static Var FN_LOADER_VAR = Var.intern(CLOJURE_NS,
Symbol.intern("*fn-loader*"), null).setDynamic();
static final Var PRINT_INITIALIZED = Var.intern(CLOJURE_NS,
Symbol.intern("print-initialized"));
static final Var PR_ON = Var.intern(CLOJURE_NS, Symbol.intern("pr-on"));
// final static Var IMPORTS = Var.intern(CLOJURE_NS,
// Symbol.intern("*imports*"), DEFAULT_IMPORTS);
final static IFn inNamespace = new AFn() {
public Object invoke(Object arg1) {
Symbol nsname = (Symbol) arg1;
Namespace ns = Namespace.findOrCreate(nsname);
CURRENT_NS.set(ns);
return ns;
}
};
final static IFn bootNamespace = new AFn() {
public Object invoke(Object __form, Object __env, Object arg1) {
Symbol nsname = (Symbol) arg1;
Namespace ns = Namespace.findOrCreate(nsname);
CURRENT_NS.set(ns);
return ns;
}
};
public static List processCommandLine(String[] args) {
List arglist = Arrays.asList(args);
int split = arglist.indexOf("--");
if (split >= 0) {
CMD_LINE_ARGS.bindRoot(RT.seq(arglist.subList(split + 1, args.length)));
return arglist.subList(0, split);
}
return arglist;
}
public static void dispatchInMain(final AFn fn) {
Var.pushThreadBindings(mapUniqueKeys(RT.var("clojure.core", "force-main-thread"), true));
try {
if (ObjC.objc) {
dispatchInMainNative(fn);
} else {
fn.invoke();
}
} catch (Exception e) {
Var.popThreadBindings();
}
}
public static native void dispatchInMainNative(final AFn fn) /*-[
if ([NSThread isMainThread]) {
[fn invoke];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[fn invoke];
});
}
]-*/;
public static void dispatchInMainSync(final AFn fn) {
Var.pushThreadBindings(mapUniqueKeys(RT.var("clojure.core", "force-main-thread"), true));
try {
if (ObjC.objc) {
dispatchInMainSyncNative(fn);
} else {
fn.invoke();
}
} catch (Exception e) {
Var.popThreadBindings();
}
}
public static native void dispatchInMainSyncNative(final AFn fn) /*-[
if ([NSThread isMainThread]) {
[fn invoke];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[fn invoke];
});
}
]-*/;
// duck typing stderr plays nice with e.g. swank
public static PrintWriter errPrintWriter() {
Writer w = (Writer) ERR.deref();
if (w instanceof PrintWriter) {
return (PrintWriter) w;
} else {
return new PrintWriter(w);
}
}
static public final Object[] EMPTY_ARRAY = new Object[] {};
static public final Comparator DEFAULT_COMPARATOR = new DefaultComparator();
private static final class DefaultComparator implements Comparator,
Serializable {
public int compare(Object o1, Object o2) {
return Util.compare(o1, o2);
}
private Object readResolve() throws ObjectStreamException {
// ensures that we aren't hanging onto a new default comparator for every
// sorted set, etc., we deserialize
return DEFAULT_COMPARATOR;
}
}
public static boolean forceClass = false;
static {
Keyword arglistskw = Keyword.intern(null, "arglists");
Symbol namesym = Symbol.intern("name");
OUT.setTag(Symbol.intern("java.io.Writer"));
CURRENT_NS.setTag(Symbol.intern("clojure.lang.Namespace"));
AGENT.setMeta(map(DOC_KEY,
"The agent currently running an action on this thread, else nil"));
AGENT.setTag(Symbol.intern("clojure.lang.Agent"));
MATH_CONTEXT.setTag(Symbol.intern("java.math.MathContext"));
Var nv = Var.intern(CLOJURE_NS, NAMESPACE, bootNamespace);
nv.setMacro();
Var v;
v = Var.intern(CLOJURE_NS, IN_NAMESPACE, inNamespace);
v.setMeta(map(
DOC_KEY,
"Sets *ns* to the namespace named by the symbol, creating it if needed.",
arglistskw, list(vector(namesym))));
v = Var.intern(CLOJURE_NS, LOAD_FILE, new AFn() {
public Object invoke(Object arg1) {
try {
return Compiler.loadFile((String) arg1);
} catch (IOException e) {
throw Util.sneakyThrow(e);
}
}
});
v.setMeta(map(
DOC_KEY,
"Sequentially read and evaluate the set of forms contained in the file.",
arglistskw, list(vector(namesym))));
try {
doInit();
if (ObjC.objc) {
loadFns();
} else {
Var.maybeLoadFromClass("clojure.core", "ns");
Var.maybeLoadFromClass("clojure.core", "in-ns");
}
} catch (Exception e) {
throw Util.sneakyThrow(e);
}
}
private static void doInit() {
try {
if (!ObjC.objc) {
RT.load("clojure/core");
Var.pushThreadBindings(RT.mapUniqueKeys(CURRENT_NS, CURRENT_NS.deref(),
WARN_ON_REFLECTION, WARN_ON_REFLECTION.deref(), RT.UNCHECKED_MATH,
RT.UNCHECKED_MATH.deref()));
try {
Symbol USER = Symbol.intern("user");
Symbol CLOJURE = Symbol.intern("clojure.core");
Var in_ns = var("clojure.core", "in-ns");
Var refer = var("clojure.core", "refer");
in_ns.invoke(USER);
refer.invoke(CLOJURE);
maybeLoadResourceScript("user.clj");
} finally {
Var.popThreadBindings();
}
}
} catch (Exception e) {
throw Util.sneakyThrow(e);
}
}
private native static void loadFns() /*-[
Clojurecore_ns_get_VAR_();
]-*/;
static public void addURL(Object url) throws MalformedURLException{
URL u = (url instanceof String) ? (new URL((String) url)) : (URL) url;
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if(ccl instanceof DynamicClassLoader)
((DynamicClassLoader)ccl).addURL(u);
else
throw new IllegalAccessError("Context classloader is not a DynamicClassLoader");
}
static public Keyword keyword(String ns, String name){
return Keyword.intern((Symbol.intern(ns, name)));
}
static public Var var(String ns, String name){
return Var.intern(Namespace.findOrCreate(Symbol.intern(null, ns)), Symbol.intern(null, name));
}
static public Var var(String ns, String name, Object init){
return Var.intern(Namespace.findOrCreate(Symbol.intern(null, ns)), Symbol.intern(null, name), init);
}
public static void loadResourceScript(String name) throws IOException{
loadResourceScript(name, true);
}
public static void maybeLoadResourceScript(String name) throws IOException{
loadResourceScript(name, false);
}
public static void loadResourceScript(String name, boolean failIfNotFound) throws IOException{
loadResourceScript(RT.class, name, failIfNotFound);
}
public static void loadResourceScript(Class c, String name) throws IOException{
loadResourceScript(c, name, true);
}
public static void loadResourceScript(Class c, String name, boolean failIfNotFound) throws IOException{
int slash = name.lastIndexOf('/');
String file = slash >= 0 ? name.substring(slash + 1) : name;
InputStream ins = resourceAsStream(baseLoader(), name);
if(ins != null) {
try {
Compiler.load(new InputStreamReader(ins, UTF8), name, file);
}
finally {
ins.close();
}
}
else if(failIfNotFound) {
throw new FileNotFoundException("Could not locate Clojure resource on classpath: " + name);
}
}
static public void init() {
RT.errPrintWriter().println("No need to call RT.init() anymore");
}
static public long lastModified(URL url, String libfile) throws IOException{
if (url.getProtocol().equals("jar")) {
return (Long) Reflector.invokeInstanceMethod(Reflector
.invokeInstanceMethod(Reflector.invokeInstanceMethod(Reflector
.invokeInstanceMethod(url, "openConnection", new Object[0]),
"getJarFile", new Object[0]), "getEntry",
new Object[] { libfile }), "getTime", new Object[0]);
} else {
return (Long) Reflector.invokeInstanceMethod(
Reflector.invokeInstanceMethod(url, "openConnection", new Object[0]),
"getLastModified", new Object[0]);
}
}
static void compile(String cljfile) throws IOException{
InputStream ins = resourceAsStream(baseLoader(), cljfile);
if(ins != null) {
try {
Compiler.compile(new InputStreamReader(ins, UTF8), cljfile,
cljfile.substring(1 + cljfile.lastIndexOf("/")));
}
finally {
ins.close();
}
}
else
throw new FileNotFoundException("Could not locate Clojure resource on classpath: " + cljfile);
}
static public void load(String scriptbase) throws IOException, ClassNotFoundException{
load(scriptbase, true);
}
static public int nextID(){
return id.getAndIncrement();
}
// Load a library in the System ClassLoader instead of Clojure's own.
public static void loadLibrary(String libname){
Reflector.invokeStaticMethod(System.class, "loadLibrary", new Object[]{libname});
}
////////////// Collections support /////////////////////////////////
private static final int CHUNK_SIZE = 32;
public static ISeq chunkIteratorSeq(final Iterator iter){
if(iter.hasNext()) {
return new LazySeq(new AFn() {
public Object invoke() {
Object[] arr = new Object[CHUNK_SIZE];
int n = 0;
while(iter.hasNext() && n < CHUNK_SIZE)
arr[n++] = iter.next();
return new ChunkedCons(new ArrayChunk(arr, 0, n), chunkIteratorSeq(iter));
}
});
}
return null;
}
static public ISeq seq(Object coll){
if(coll instanceof ASeq)
return (ASeq) coll;
else if(coll instanceof LazySeq)
return ((LazySeq) coll).seq();
else
return seqFrom(coll);
}
static ISeq seqFrom(Object coll){
if(coll instanceof Seqable)
return ((Seqable) coll).seq();
else if(coll == null)
return null;
else if(coll instanceof Iterable)
return chunkIteratorSeq(((Iterable) coll).iterator());
else if(coll.getClass().isArray())
return ArraySeq.createFromObject(coll);
else if(coll instanceof CharSequence)
return StringSeq.create((CharSequence) coll);
else if(coll instanceof Map)
return seq(((Map) coll).entrySet());
else {
Class c = coll.getClass();
Class sc = c.getSuperclass();
throw new IllegalArgumentException("Don't know how to create ISeq from: " + c.getName());
}
}
static public Iterator iter(Object coll){
if(coll instanceof Iterable)
return ((Iterable)coll).iterator();
else if(coll == null)
return new Iterator(){
public boolean hasNext(){
return false;
}
public Object next(){
throw new NoSuchElementException();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
else if(coll instanceof Map){
return ((Map)coll).entrySet().iterator();
}
else if(coll instanceof String){
final String s = (String) coll;
return new Iterator(){
int i = 0;
public boolean hasNext(){
return i < s.length();
}
public Object next(){
return s.charAt(i++);
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
else if(coll.getClass().isArray()){
return ArrayIter.createFromObject(coll);
}else
return iter(seq(coll));
}
static public Object seqOrElse(Object o) {
return seq(o) == null ? null : o;
}
static public ISeq keys(Object coll){
if(coll instanceof IPersistentMap)
return APersistentMap.KeySeq.createFromMap((IPersistentMap)coll);
else
return APersistentMap.KeySeq.create(seq(coll));
}
static public ISeq vals(Object coll){
if(coll instanceof IPersistentMap)
return APersistentMap.ValSeq.createFromMap((IPersistentMap)coll);
else
return APersistentMap.ValSeq.create(seq(coll));
}
static public IPersistentMap meta(Object x){
if(x instanceof IMeta)
return ((IMeta) x).meta();
return null;
}
public static int count(Object o){
if(o instanceof Counted)
return ((Counted) o).count();
return countFrom(Util.ret1(o, o = null));
}
static int countFrom(Object o){
if(o == null)
return 0;
else if(o instanceof IPersistentCollection) {
ISeq s = seq(o);
o = null;
int i = 0;
for(; s != null; s = s.next()) {
if(s.getClass() != Cons.class && s instanceof Counted)
return i + s.count();
i++;
}
return i;
}
else if(o instanceof CharSequence)
return ((CharSequence) o).length();
else if(o instanceof Collection)
return ((Collection) o).size();
else if(o instanceof Map)
return ((Map) o).size();
else if (o instanceof Map.Entry)
return 2;
else if(o.getClass().isArray())
return Array.getLength(o);
throw new UnsupportedOperationException("count not supported on this type: " + o.getClass().getSimpleName());
}
static public IPersistentCollection conj(IPersistentCollection coll, Object x){
if(coll == null)
return new PersistentList(x);
return coll.cons(x);
}
static public ISeq cons(Object x, Object coll){
//ISeq y = seq(coll);
if(coll == null)
return new PersistentList(x);
else if(coll instanceof ISeq)
return new Cons(x, (ISeq) coll);
else
return new Cons(x, seq(coll));
}
static public Object first(Object x){
if(x instanceof ISeq)
return ((ISeq) x).first();
ISeq seq = seq(x);
if(seq == null)
return null;
return seq.first();
}
static public Object second(Object x){
return first(next(x));
}
static public Object third(Object x){
return first(next(next(x)));
}
static public Object fourth(Object x){
return first(next(next(next(x))));
}
static public ISeq next(Object x){
if(x instanceof ISeq)
return ((ISeq) x).next();
ISeq seq = seq(x);
if(seq == null)
return null;
return seq.next();
}
static public ISeq more(Object x){
if(x instanceof ISeq)
return ((ISeq) x).more();
ISeq seq = seq(x);
if(seq == null)
return PersistentList.EMPTY;
return seq.more();
}
//static public Seqable more(Object x){
// Seqable ret = null;
// if(x instanceof ISeq)
// ret = ((ISeq) x).more();
// else
// {
// ISeq seq = seq(x);
// if(seq == null)
// ret = PersistentList.EMPTY;
// else
// ret = seq.more();
// }
// if(ret == null)
// ret = PersistentList.EMPTY;
// return ret;
//}
static public Object peek(Object x){
if(x == null)
return null;
return ((IPersistentStack) x).peek();
}
static public Object pop(Object x){
if(x == null)
return null;
return ((IPersistentStack) x).pop();
}
static public Object get(Object coll, Object key){
if(coll instanceof ILookup)
return ((ILookup) coll).valAt(key);
return getFrom(coll, key);
}
static Object getFrom(Object coll, Object key){
if(coll == null)
return null;
else if(coll instanceof Map) {
Map m = (Map) coll;
return m.get(key);
}
else if(coll instanceof IPersistentSet) {
IPersistentSet set = (IPersistentSet) coll;
return set.get(key);
}
else if(key instanceof Number && (coll instanceof String || coll.getClass().isArray())) {
int n = ((Number) key).intValue();
if(n >= 0 && n < count(coll))
return nth(coll, n);
return null;
}
return null;
}
static public Object get(Object coll, Object key, Object notFound){
if(coll instanceof ILookup)
return ((ILookup) coll).valAt(key, notFound);
return getFrom(coll, key, notFound);
}
static Object getFrom(Object coll, Object key, Object notFound){
if(coll == null)
return notFound;
else if(coll instanceof Map) {
Map m = (Map) coll;
if(m.containsKey(key))
return m.get(key);
return notFound;
}
else if(coll instanceof IPersistentSet) {
IPersistentSet set = (IPersistentSet) coll;
if(set.contains(key))
return set.get(key);
return notFound;
}
else if(key instanceof Number && (coll instanceof String || coll.getClass().isArray())) {
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll) ? nth(coll, n) : notFound;
}
return notFound;
}
static public Associative assoc(Object coll, Object key, Object val){
if(coll == null)
return new PersistentArrayMap(new Object[]{key, val});
return ((Associative) coll).assoc(key, val);
}
static public Object contains(Object coll, Object key){
if(coll == null)
return F;
else if(coll instanceof Associative)
return ((Associative) coll).containsKey(key) ? T : F;
else if(coll instanceof IPersistentSet)
return ((IPersistentSet) coll).contains(key) ? T : F;
else if(coll instanceof Map) {
Map m = (Map) coll;
return m.containsKey(key) ? T : F;
}
else if(coll instanceof Set) {
Set s = (Set) coll;
return s.contains(key) ? T : F;
}
else if(key instanceof Number && (coll instanceof String || coll.getClass().isArray())) {
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll);
}
throw new IllegalArgumentException("contains? not supported on type: " + coll.getClass().getName());
}
static public Object find(Object coll, Object key){
if(coll == null)
return null;
else if(coll instanceof Associative)
return ((Associative) coll).entryAt(key);
else {
Map m = (Map) coll;
if(m.containsKey(key))
return new MapEntry(key, m.get(key));
return null;
}
}
//takes a seq of key,val,key,val
//returns tail starting at val of matching key if found, else null
static public ISeq findKey(Keyword key, ISeq keyvals) {
while(keyvals != null) {
ISeq r = keyvals.next();
if(r == null)
throw Util.runtimeException("Malformed keyword argslist");
if(keyvals.first() == key)
return r;
keyvals = r.next();
}
return null;
}
static public Object dissoc(Object coll, Object key) {
if(coll == null)
return null;
return ((IPersistentMap) coll).without(key);
}
static public Object nth(Object coll, int n){
if(coll instanceof Indexed)
return ((Indexed) coll).nth(n);
return nthFrom(Util.ret1(coll, coll = null), n);
}
static Object nthFrom(Object coll, int n){
if(coll == null)
return null;
else if(coll instanceof CharSequence)
return Character.valueOf(((CharSequence) coll).charAt(n));
else if(coll.getClass().isArray())
return Reflector.prepRet(coll.getClass().getComponentType(),Array.get(coll, n));
else if(coll instanceof RandomAccess)
return ((List) coll).get(n);
else if(coll instanceof Matcher)
return ((Matcher) coll).group(n);
else if(coll instanceof Map.Entry) {
Map.Entry e = (Map.Entry) coll;
if(n == 0)
return e.getKey();
else if(n == 1)
return e.getValue();
throw new IndexOutOfBoundsException();
}
else if(coll instanceof Sequential) {
ISeq seq = RT.seq(coll);
coll = null;
for(int i = 0; i <= n && seq != null; ++i, seq = seq.next()) {
if(i == n)
return seq.first();
}
throw new IndexOutOfBoundsException();
}
else
throw new UnsupportedOperationException(
"nth not supported on this type: " + coll.getClass().getSimpleName());
}
static public Object nth(Object coll, int n, Object notFound){
if(coll instanceof Indexed) {
Indexed v = (Indexed) coll;
return v.nth(n, notFound);
}
return nthFrom(coll, n, notFound);
}
static public Object objcClass(String name) {
if (ObjC.objc) {
return nativeObjcClass(name);
} else {
return new ObjCClass(name);
}
}
static public native Object nativeObjcClass(String name) /*-[
return NSClassFromString(name);
]-*/;
public static native void loadiOS(String scriptbase) /*-[
IOSObjectArray *parts = [[scriptbase replaceAll:@"-" withReplacement:@"_"] split:@"/"];
NSString *classname = @"";
for (int n = 0; n < parts.length - 1; n++) {
NSString *s = (NSString*)[parts objectAtIndex:n];
s = [[[s substringToIndex:1] uppercaseString] stringByAppendingString:[s substringFromIndex:1]];
classname = [classname stringByAppendingString:s];
}
classname = [classname stringByAppendingString:[parts objectAtIndex:parts.length-1]];
classname = [classname stringByAppendingString:@"__init"];
Class c = NSClassFromString(classname);
if (c == nil) {
NSLog(@"Error loading %@", scriptbase);
} else {
[c description]; // Force initialize
}
return;
]-*/;
static public void load(String scriptbase, boolean failIfNotFound) throws IOException, ClassNotFoundException{
if (ObjC.objc) {
loadiOS(scriptbase);
} else {
String classfile = scriptbase + LOADER_SUFFIX + ".class";
String cljfile = scriptbase + ".clj";
String scriptfile = cljfile;
URL classURL = getResource(baseLoader(),classfile);
URL cljURL = getResource(baseLoader(), scriptfile);
if(cljURL == null) {
scriptfile = scriptbase + ".cljc";
cljURL = getResource(baseLoader(), scriptfile);
}
boolean loaded = false;
if((classURL != null &&
(cljURL == null
|| lastModified(classURL, classfile) > lastModified(cljURL, scriptfile)))
|| classURL == null) {
try {
Var.pushThreadBindings(
RT.mapUniqueKeys(CURRENT_NS, CURRENT_NS.deref(), Compiler.NEXT_ID, new Atom(1),
WARN_ON_REFLECTION, WARN_ON_REFLECTION.deref()
,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()));
loaded = (loadClassForName(scriptbase.replace('/', '.') + LOADER_SUFFIX) != null);
}
finally {
Var.popThreadBindings();
}
}
if(!loaded && cljURL != null) {
if(booleanCast(Compiler.COMPILE_FILES.deref()))
compile(scriptfile);
else
loadResourceScript(RT.class, scriptfile);
}
else if(!loaded && failIfNotFound)
throw new FileNotFoundException(String.format("Could not locate %s or %s on classpath.%s", classfile, cljfile,
scriptbase.contains("_") ? " Please check that namespaces with dashes use underscores in the Clojure file name." : ""));
}
}
static Object nthFrom(Object coll, int n, Object notFound) {
if (coll == null)
return notFound;
else if (n < 0)
return notFound;
else if (coll instanceof CharSequence) {
CharSequence s = (CharSequence) coll;
if (n < s.length())
return Character.valueOf(s.charAt(n));
return notFound;
} else if (coll.getClass().isArray()) {
if (n < Array.getLength(coll))
return Reflector.prepRet(coll.getClass().getComponentType(),
Array.get(coll, n));
return notFound;
} else if (coll instanceof RandomAccess) {
List list = (List) coll;
if (n < list.size())
return list.get(n);
return notFound;
} else if (coll instanceof Matcher) {
Matcher m = (Matcher) coll;
if (n < m.groupCount())
return m.group(n);
return notFound;
} else if (coll instanceof Map.Entry) {
Map.Entry e = (Map.Entry) coll;
if (n == 0)
return e.getKey();
else if (n == 1)
return e.getValue();
return notFound;
} else if (coll instanceof Sequential) {
ISeq seq = RT.seq(coll);
coll = null;
for (int i = 0; i <= n && seq != null; ++i, seq = seq.next()) {
if (i == n)
return seq.first();
}
return notFound;
} else
throw new UnsupportedOperationException(
"nth not supported on this type: " + coll.getClass().getSimpleName());
}
static public Object assocN(int n, Object val, Object coll) {
if (coll == null)
return null;
else if (coll instanceof IPersistentVector)
return ((IPersistentVector) coll).assocN(n, val);
else if (coll instanceof Object[]) {
// hmm... this is not persistent
Object[] array = ((Object[]) coll);
array[n] = val;
return array;
} else
return null;
}
static boolean hasTag(Object o, Object tag) {
return Util.equals(tag, RT.get(RT.meta(o), TAG_KEY));
}
/**
* ********************* Boxing/casts ******************************
*/
static public Object box(Object x) {
return x;
}
static public Character box(char x) {
return Character.valueOf(x);
}
static public Object box(boolean x) {
return x ? T : F;
}
static public Object box(Boolean x) {
return x;// ? T : null;
}
static public Number box(byte x) {
return x;// Num.from(x);
}
static public Number box(short x) {
return x;// Num.from(x);
}
static public Number box(int x) {
return x;// Num.from(x);
}
static public Number box(long x) {
return x;// Num.from(x);
}
static public Number box(float x) {
return x;// Num.from(x);
}
static public Number box(double x) {
return x;// Num.from(x);
}
static public char charCast(Object x) {
if (x instanceof Character)
return ((Character) x).charValue();
long n = ((Number) x).longValue();
if (n < Character.MIN_VALUE || n > Character.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for char: " + x);
return (char) n;
}
static public char charCast(byte x) {
char i = (char) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for char: " + x);
return i;
}
static public char charCast(short x) {
char i = (char) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for char: " + x);
return i;
}
static public char charCast(char x) {
return x;
}
static public char charCast(int x) {
char i = (char) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for char: " + x);
return i;
}
static public char charCast(long x) {
char i = (char) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for char: " + x);
return i;
}
static public char charCast(float x) {
if (x >= Character.MIN_VALUE && x <= Character.MAX_VALUE)
return (char) x;
throw new IllegalArgumentException("Value out of range for char: " + x);
}
static public char charCast(double x) {
if (x >= Character.MIN_VALUE && x <= Character.MAX_VALUE)
return (char) x;
throw new IllegalArgumentException("Value out of range for char: " + x);
}
static public boolean booleanCast(Object x) {
if (x instanceof Boolean)
return ((Boolean) x).booleanValue();
return x != null;
}
static public boolean booleanCast(boolean x) {
return x;
}
static public byte byteCast(Object x) {
if (x instanceof Byte)
return ((Byte) x).byteValue();
long n = longCast(x);
if (n < Byte.MIN_VALUE || n > Byte.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for byte: " + x);
return (byte) n;
}
static public byte byteCast(byte x) {
return x;
}
static public byte byteCast(short x) {
byte i = (byte) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for byte: " + x);
return i;
}
static public byte byteCast(int x) {
byte i = (byte) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for byte: " + x);
return i;
}
static public byte byteCast(long x) {
byte i = (byte) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for byte: " + x);
return i;
}
static public byte byteCast(float x) {
if (x >= Byte.MIN_VALUE && x <= Byte.MAX_VALUE)
return (byte) x;
throw new IllegalArgumentException("Value out of range for byte: " + x);
}
static public byte byteCast(double x) {
if (x >= Byte.MIN_VALUE && x <= Byte.MAX_VALUE)
return (byte) x;
throw new IllegalArgumentException("Value out of range for byte: " + x);
}
static public short shortCast(Object x) {
if (x instanceof Short)
return ((Short) x).shortValue();
long n = longCast(x);
if (n < Short.MIN_VALUE || n > Short.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for short: " + x);
return (short) n;
}
static public short shortCast(byte x) {
return x;
}
static public short shortCast(short x) {
return x;
}
static public short shortCast(int x) {
short i = (short) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for short: " + x);
return i;
}
static public short shortCast(long x) {
short i = (short) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for short: " + x);
return i;
}
static public short shortCast(float x) {
if (x >= Short.MIN_VALUE && x <= Short.MAX_VALUE)
return (short) x;
throw new IllegalArgumentException("Value out of range for short: " + x);
}
static public short shortCast(double x) {
if (x >= Short.MIN_VALUE && x <= Short.MAX_VALUE)
return (short) x;
throw new IllegalArgumentException("Value out of range for short: " + x);
}
static public int intCast(Object x) {
if (x == null) {
throw Util.sneakyThrow(new NullPointerException());
}
if (x instanceof Integer)
return ((Integer) x).intValue();
if (x instanceof Number) {
long n = longCast(x);
return intCast(n);
}
if (x instanceof Character) {
return ((Character) x).charValue();
}
throw Util.sneakyThrow(new ClassCastException());
}
static public int intCast(char x) {
return x;
}
static public int intCast(byte x) {
return x;
}
static public int intCast(short x) {
return x;
}
static public int intCast(int x) {
return x;
}
static public int intCast(float x) {
if (x < Integer.MIN_VALUE || x > Integer.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for int: " + x);
return (int) x;
}
static public int intCast(long x) {
int i = (int) x;
if (i != x)
throw new IllegalArgumentException("Value out of range for int: " + x);
return i;
}
static public int intCast(double x) {
if (x < Integer.MIN_VALUE || x > Integer.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for int: " + x);
return (int) x;
}
static public long longCast(Object x) {
if (x instanceof Integer || x instanceof Long)
return ((Number) x).longValue();
else if (x instanceof BigInt) {
BigInt bi = (BigInt) x;
if (bi.bipart == null)
return bi.lpart;
else
throw new IllegalArgumentException("Value out of range for long: " + x);
} else if (x instanceof BigInteger) {
BigInteger bi = (BigInteger) x;
if (bi.bitLength() < 64)
return bi.longValue();
else
throw new IllegalArgumentException("Value out of range for long: " + x);
} else if (x instanceof Byte || x instanceof Short)
return ((Number) x).longValue();
else if (x instanceof Ratio)
return longCast(((Ratio) x).bigIntegerValue());
else if (x instanceof Character)
return longCast(((Character) x).charValue());
else
return longCast(((Number) x).doubleValue());
}
static public long longCast(byte x) {
return x;
}
static public long longCast(short x) {
return x;
}
static public long longCast(int x) {
return x;
}
static public long longCast(float x) {
if (x < Long.MIN_VALUE || x > Long.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for long: " + x);
return (long) x;
}
static public long longCast(long x) {
return x;
}
static public long longCast(double x) {
if (x < Long.MIN_VALUE || x > Long.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for long: " + x);
return (long) x;
}
static public float floatCast(Object x) {
if (x instanceof Float)
return ((Float) x).floatValue();
double n = ((Number) x).doubleValue();
if (n < -Float.MAX_VALUE || n > Float.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for float: " + x);
return (float) n;
}
static public float floatCast(byte x) {
return x;
}
static public float floatCast(short x) {
return x;
}
static public float floatCast(int x) {
return x;
}
static public float floatCast(float x) {
return x;
}
static public float floatCast(long x) {
return x;
}
static public float floatCast(double x) {
if (x < -Float.MAX_VALUE || x > Float.MAX_VALUE)
throw new IllegalArgumentException("Value out of range for float: " + x);
return (float) x;
}
static public double doubleCast(Object x) {
return ((Number) x).doubleValue();
}
static public double doubleCast(byte x) {
return x;
}
static public double doubleCast(short x) {
return x;
}
static public double doubleCast(int x) {
return x;
}
static public double doubleCast(float x) {
return x;
}
static public double doubleCast(long x) {
return x;
}
static public double doubleCast(double x) {
return x;
}
static public byte uncheckedByteCast(Object x) {
return ((Number) x).byteValue();
}
static public byte uncheckedByteCast(byte x) {
return x;
}
static public byte uncheckedByteCast(short x) {
return (byte) x;
}
static public byte uncheckedByteCast(int x) {
return (byte) x;
}
static public byte uncheckedByteCast(long x) {
return (byte) x;
}
static public byte uncheckedByteCast(float x) {
return (byte) x;
}
static public byte uncheckedByteCast(double x) {
return (byte) x;
}
static public short uncheckedShortCast(Object x) {
return ((Number) x).shortValue();
}
static public short uncheckedShortCast(byte x) {
return x;
}
static public short uncheckedShortCast(short x) {
return x;
}
static public short uncheckedShortCast(int x) {
return (short) x;
}
static public short uncheckedShortCast(long x) {
return (short) x;
}
static public short uncheckedShortCast(float x) {
return (short) x;
}
static public short uncheckedShortCast(double x) {
return (short) x;
}
static public char uncheckedCharCast(Object x) {
if (x instanceof Character)
return ((Character) x).charValue();
return (char) ((Number) x).longValue();
}
static public char uncheckedCharCast(byte x) {
return (char) x;
}
static public char uncheckedCharCast(short x) {
return (char) x;
}
static public char uncheckedCharCast(char x) {
return x;
}
static public char uncheckedCharCast(int x) {
return (char) x;
}
static public char uncheckedCharCast(long x) {
return (char) x;
}
static public char uncheckedCharCast(float x) {
return (char) x;
}
static public char uncheckedCharCast(double x) {
return (char) x;
}
static public int uncheckedIntCast(Object x) {
if (x instanceof Number)
return ((Number) x).intValue();
return ((Character) x).charValue();
}
static public int uncheckedIntCast(byte x) {
return x;
}
static public int uncheckedIntCast(short x) {
return x;
}
static public int uncheckedIntCast(char x) {
return x;
}
static public int uncheckedIntCast(int x) {
return x;
}
static public int uncheckedIntCast(long x) {
return (int) x;
}
static public int uncheckedIntCast(float x) {
return (int) x;
}
static public int uncheckedIntCast(double x) {
return (int) x;
}
static public long uncheckedLongCast(Object x) {
return ((Number) x).longValue();
}
static public long uncheckedLongCast(byte x) {
return x;
}
static public long uncheckedLongCast(short x) {
return x;
}
static public long uncheckedLongCast(int x) {
return x;
}
static public long uncheckedLongCast(long x) {
return x;
}
static public long uncheckedLongCast(float x) {
return (long) x;
}
static public long uncheckedLongCast(double x) {
return (long) x;
}
static public float uncheckedFloatCast(Object x) {
return ((Number) x).floatValue();
}
static public float uncheckedFloatCast(byte x) {
return x;
}
static public float uncheckedFloatCast(short x) {
return x;
}
static public float uncheckedFloatCast(int x) {
return x;
}
static public float uncheckedFloatCast(long x) {
return x;
}
static public float uncheckedFloatCast(float x) {
return x;
}
static public float uncheckedFloatCast(double x) {
return (float) x;
}
static public double uncheckedDoubleCast(Object x) {
return ((Number) x).doubleValue();
}
static public double uncheckedDoubleCast(byte x) {
return x;
}
static public double uncheckedDoubleCast(short x) {
return x;
}
static public double uncheckedDoubleCast(int x) {
return x;
}
static public double uncheckedDoubleCast(long x) {
return x;
}
static public double uncheckedDoubleCast(float x) {
return x;
}
static public double uncheckedDoubleCast(double x) {
return x;
}
static public IPersistentMap map(Object... init) {
if (init == null)
return PersistentArrayMap.EMPTY;
else if (init.length <= PersistentArrayMap.HASHTABLE_THRESHOLD)
return PersistentArrayMap.createWithCheck(init);
return PersistentHashMap.createWithCheck(init);
}
static public IPersistentMap mapUniqueKeys(Object... init) {
if (init == null)
return PersistentArrayMap.EMPTY;
else if (init.length <= PersistentArrayMap.HASHTABLE_THRESHOLD)
return new PersistentArrayMap(init);
return PersistentHashMap.create(init);
}
static public IPersistentSet set(Object... init) {
return PersistentHashSet.createWithCheck(init);
}
static public IPersistentVector vector(Object... init) {
return LazilyPersistentVector.createOwning(init);
}
static public IPersistentVector subvec(IPersistentVector v, int start, int end) {
if (end < start || start < 0 || end > v.count())
throw new IndexOutOfBoundsException();
if (start == end)
return PersistentVector.EMPTY;
return new APersistentVector.SubVector(null, v, start, end);
}
/**
* **************************************** list support
* *******************************
*/
static public ISeq list() {
return null;
}
static public ISeq list(Object arg1) {
return new PersistentList(arg1);
}
static public ISeq list(Object arg1, Object arg2) {
return listStar(arg1, arg2, null);
}
static public ISeq list(Object arg1, Object arg2, Object arg3) {
return listStar(arg1, arg2, arg3, null);
}
static public ISeq list(Object arg1, Object arg2, Object arg3, Object arg4) {
return listStar(arg1, arg2, arg3, arg4, null);
}
static public ISeq list(Object arg1, Object arg2, Object arg3, Object arg4,
Object arg5) {
return listStar(arg1, arg2, arg3, arg4, arg5, null);
}
static public ISeq listStar(Object arg1, ISeq rest) {
return (ISeq) cons(arg1, rest);
}
static public ISeq listStar(Object arg1, Object arg2, ISeq rest) {
return (ISeq) cons(arg1, cons(arg2, rest));
}
static public ISeq listStar(Object arg1, Object arg2, Object arg3, ISeq rest) {
return (ISeq) cons(arg1, cons(arg2, cons(arg3, rest)));
}
static public ISeq listStar(Object arg1, Object arg2, Object arg3,
Object arg4, ISeq rest) {
return (ISeq) cons(arg1, cons(arg2, cons(arg3, cons(arg4, rest))));
}
static public ISeq listStar(Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, ISeq rest) {
return (ISeq) cons(arg1,
cons(arg2, cons(arg3, cons(arg4, cons(arg5, rest)))));
}
static public ISeq arrayToList(Object[] a) {
ISeq ret = null;
for (int i = a.length - 1; i >= 0; --i)
ret = (ISeq) cons(a[i], ret);
return ret;
}
static public Object[] object_array(Object sizeOrSeq) {
if (sizeOrSeq instanceof Number)
return new Object[((Number) sizeOrSeq).intValue()];
else {
ISeq s = RT.seq(sizeOrSeq);
int size = RT.count(s);
Object[] ret = new Object[size];
for (int i = 0; i < size && s != null; i++, s = s.next())
ret[i] = s.first();
return ret;
}
}
static public Object[] toArray(Object coll) {
if (coll == null)
return EMPTY_ARRAY;
else if (coll instanceof Object[])
return (Object[]) coll;
else if (coll instanceof Collection)
return ((Collection) coll).toArray();
else if(coll instanceof Iterable) {
ArrayList ret = new ArrayList();
for(Object o : (Iterable)coll)
ret.add(o);
return ret.toArray();
} else if (coll instanceof Map)
return ((Map) coll).entrySet().toArray();
else if (coll instanceof String) {
char[] chars = ((String) coll).toCharArray();
Object[] ret = new Object[chars.length];
for (int i = 0; i < chars.length; i++)
ret[i] = chars[i];
return ret;
} else if (coll.getClass().isArray()) {
ISeq s = (seq(coll));
Object[] ret = new Object[count(s)];
for (int i = 0; i < ret.length; i++, s = s.next())
ret[i] = s.first();
return ret;
} else
throw Util.runtimeException("Unable to convert: " + coll.getClass()
+ " to Object[]");
}
static public Object[] seqToArray(ISeq seq) {
int len = length(seq);
Object[] ret = new Object[len];
for (int i = 0; seq != null; ++i, seq = seq.next())
ret[i] = seq.first();
return ret;
}
// supports java Collection.toArray(T[])
static public Object[] seqToPassedArray(ISeq seq, Object[] passed) {
Object[] dest = passed;
int len = count(seq);
if (len > dest.length) {
dest = (Object[]) Array.newInstance(passed.getClass().getComponentType(),
len);
}
for (int i = 0; seq != null; ++i, seq = seq.next())
dest[i] = seq.first();
if (len < passed.length) {
dest[len] = null;
}
return dest;
}
static public Object seqToTypedArray(ISeq seq) {
Class type = (seq != null) ? seq.first().getClass() : Object.class;
return seqToTypedArray(type, seq);
}
static public Object seqToTypedArray(Class type, ISeq seq) {
Object ret = Array.newInstance(type, length(seq));
if (type == Integer.TYPE) {
for (int i = 0; seq != null; ++i, seq = seq.next()) {
Array.set(ret, i, intCast(seq.first()));
}
} else if (type == Byte.TYPE) {
for (int i = 0; seq != null; ++i, seq = seq.next()) {
Array.set(ret, i, byteCast(seq.first()));
}
} else if (type == Float.TYPE) {
for (int i = 0; seq != null; ++i, seq = seq.next()) {
Array.set(ret, i, floatCast(seq.first()));
}
} else if (type == Short.TYPE) {
for (int i = 0; seq != null; ++i, seq = seq.next()) {
Array.set(ret, i, shortCast(seq.first()));
}
} else if (type == Character.TYPE) {
for (int i = 0; seq != null; ++i, seq = seq.next()) {
Array.set(ret, i, charCast(seq.first()));
}
} else {
for (int i = 0; seq != null; ++i, seq = seq.next()) {
Array.set(ret, i, seq.first());
}
}
return ret;
}
static public int length(ISeq list){
int i = 0;
for(ISeq c = list; c != null; c = c.next()) {
i++;
}
return i;
}
static public int boundedLength(ISeq list, int limit) {
int i = 0;
for(ISeq c = list; c != null && i <= limit; c = c.next()) {
i++;
}
return i;
}
///////////////////////////////// reader support ////////////////////////////////
static Character readRet(int ret){
if(ret == -1)
return null;
return box((char) ret);
}
static public Character readChar(Reader r) throws IOException{
int ret = r.read();
return readRet(ret);
}
static public Character peekChar(Reader r) throws IOException{
int ret;
if(r instanceof PushbackReader) {
ret = r.read();
((PushbackReader) r).unread(ret);
}
else {
r.mark(1);
ret = r.read();
r.reset();
}
return readRet(ret);
}
static public int getLineNumber(Reader r){
if(r instanceof LineNumberingPushbackReader)
return ((LineNumberingPushbackReader) r).getLineNumber();
return 0;
}
static public int getColumnNumber(Reader r){
if(r instanceof LineNumberingPushbackReader)
return ((LineNumberingPushbackReader) r).getColumnNumber();
return 0;
}
static public LineNumberingPushbackReader getLineNumberingReader(Reader r){
if(isLineNumberingReader(r))
return (LineNumberingPushbackReader) r;
return new LineNumberingPushbackReader(r);
}
static public boolean isLineNumberingReader(Reader r){
return r instanceof LineNumberingPushbackReader;
}
static public boolean isReduced(Object r){
return r instanceof Reduced;
}
static public String resolveClassNameInContext(String className){
//todo - look up in context var
return className;
}
static public boolean suppressRead(){
return booleanCast(SUPPRESS_READ.deref());
}
static public String printString(Object x){
try {
StringWriter sw = new StringWriter();
print(x, sw);
return sw.toString();
}
catch(Exception e) {
throw Util.sneakyThrow(e);
}
}
static public Object readString(String s){
return readString(s, null);
}
static public Object readString(String s, Object opts) {
PushbackReader r = new PushbackReader(new StringReader(s));
return LispReader.read(r, opts);
}
static public void print(Object x, Writer w) throws IOException{
//call multimethod
if(PRINT_INITIALIZED.isBound() && RT.booleanCast(PRINT_INITIALIZED.deref()))
PR_ON.invoke(x, w);
//*
else {
boolean readably = booleanCast(PRINT_READABLY.deref());
if(x instanceof Obj) {
Obj o = (Obj) x;
if(RT.count(o.meta()) > 0 &&
((readably && booleanCast(PRINT_META.deref()))
|| booleanCast(PRINT_DUP.deref()))) {
IPersistentMap meta = o.meta();
w.write("#^");
if(meta.count() == 1 && meta.containsKey(TAG_KEY))
print(meta.valAt(TAG_KEY), w);
else
print(meta, w);
w.write(' ');
}
}
if(x == null)
w.write("nil");
else if(x instanceof ISeq || x instanceof IPersistentList) {
w.write('(');
printInnerSeq(seq(x), w);
w.write(')');
}
else if(x instanceof String) {
String s = (String) x;
if(!readably)
w.write(s);
else {
w.write('"');
//w.write(x.toString());
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch(c) {
case '\n':
w.write("\\n");
break;
case '\t':
w.write("\\t");
break;
case '\r':
w.write("\\r");
break;
case '"':
w.write("\\\"");
break;
case '\\':
w.write("\\\\");
break;
case '\f':
w.write("\\f");
break;
case '\b':
w.write("\\b");
break;
default:
w.write(c);
}
}
w.write('"');
}
}
else if(x instanceof IPersistentMap) {
w.write('{');
for(ISeq s = seq(x); s != null; s = s.next()) {
IMapEntry e = (IMapEntry) s.first();
print(e.key(), w);
w.write(' ');
print(e.val(), w);
if(s.next() != null)
w.write(", ");
}
w.write('}');
}
else if(x instanceof IPersistentVector) {
IPersistentVector a = (IPersistentVector) x;
w.write('[');
for(int i = 0; i < a.count(); i++) {
print(a.nth(i), w);
if(i < a.count() - 1)
w.write(' ');
}
w.write(']');
}
else if(x instanceof IPersistentSet) {
w.write("#{");
for(ISeq s = seq(x); s != null; s = s.next()) {
print(s.first(), w);
if(s.next() != null)
w.write(" ");
}
w.write('}');
}
else if(x instanceof Character) {
char c = ((Character) x).charValue();
if(!readably)
w.write(c);
else {
w.write('\\');
switch(c) {
case '\n':
w.write("newline");
break;
case '\t':
w.write("tab");
break;
case ' ':
w.write("space");
break;
case '\b':
w.write("backspace");
break;
case '\f':
w.write("formfeed");
break;
case '\r':
w.write("return");
break;
default:
w.write(c);
}
}
}
else if(x instanceof Class) {
w.write("#=");
w.write(((Class) x).getName());
}
else if(x instanceof BigDecimal && readably) {
w.write(x.toString());
w.write('M');
}
else if(x instanceof BigInt && readably) {
w.write(x.toString());
w.write('N');
}
else if(x instanceof BigInteger && readably) {
w.write(x.toString());
w.write("BIGINT");
}
else if(x instanceof Var) {
Var v = (Var) x;
w.write("#=(var " + v.ns.name + "/" + v.sym + ")");
}
else if(x instanceof Pattern) {
Pattern p = (Pattern) x;
w.write("#\"" + p.pattern() + "\"");
}
else w.write(x.toString());
}
//*/
}
private static void printInnerSeq(ISeq x, Writer w) throws IOException{
for(ISeq s = x; s != null; s = s.next()) {
print(s.first(), w);
if(s.next() != null)
w.write(' ');
}
}
static public void formatAesthetic(Writer w, Object obj) throws IOException{
if(obj == null)
w.write("null");
else
w.write(obj.toString());
}
static public void formatStandard(Writer w, Object obj) throws IOException{
if(obj == null)
w.write("null");
else if(obj instanceof String) {
w.write('"');
w.write((String) obj);
w.write('"');
}
else if(obj instanceof Character) {
w.write('\\');
char c = ((Character) obj).charValue();
switch(c) {
case '\n':
w.write("newline");
break;
case '\t':
w.write("tab");
break;
case ' ':
w.write("space");
break;
case '\b':
w.write("backspace");
break;
case '\f':
w.write("formfeed");
break;
default:
w.write(c);
}
}
else
w.write(obj.toString());
}
static public Object format(Object o, String s, Object... args) throws IOException{
Writer w;
if(o == null)
w = new StringWriter();
else if(Util.equals(o, T))
w = (Writer) OUT.deref();
else
w = (Writer) o;
doFormat(w, s, ArraySeq.create(args));
if(o == null)
return w.toString();
return null;
}
static public ISeq doFormat(Writer w, String s, ISeq args) throws IOException{
for(int i = 0; i < s.length();) {
char c = s.charAt(i++);
switch(Character.toLowerCase(c)) {
case '~':
char d = s.charAt(i++);
switch(Character.toLowerCase(d)) {
case '%':
w.write('\n');
break;
case 't':
w.write('\t');
break;
case 'a':
if(args == null)
throw new IllegalArgumentException("Missing argument");
RT.formatAesthetic(w, RT.first(args));
args = RT.next(args);
break;
case 's':
if(args == null)
throw new IllegalArgumentException("Missing argument");
RT.formatStandard(w, RT.first(args));
args = RT.next(args);
break;
case '{':
int j = s.indexOf("~}", i); //note - does not nest
if(j == -1)
throw new IllegalArgumentException("Missing ~}");
String subs = s.substring(i, j);
for(ISeq sargs = RT.seq(RT.first(args)); sargs != null;)
sargs = doFormat(w, subs, sargs);
args = RT.next(args);
i = j + 2; //skip ~}
break;
case '^':
if(args == null)
return null;
break;
case '~':
w.write('~');
break;
default:
throw new IllegalArgumentException("Unsupported ~ directive: " + d);
}
break;
default:
w.write(c);
}
}
return args;
}
///////////////////////////////// values //////////////////////////
static public Object[] setValues(Object... vals){
//ThreadLocalData.setValues(vals);
if(vals.length > 0)
return vals;//[0];
return null;
}
static public ClassLoader makeClassLoader(){
return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
try{
Var.pushThreadBindings(RT.map(USE_CONTEXT_CLASSLOADER, RT.T));
// getRootClassLoader();
return new DynamicClassLoader(baseLoader());
}
finally{
Var.popThreadBindings();
}
}
});
}
static public ClassLoader baseLoader(){
if(Compiler.LOADER.isBound())
return (ClassLoader) Compiler.LOADER.deref();
else if(booleanCast(USE_CONTEXT_CLASSLOADER.deref()))
return Thread.currentThread().getContextClassLoader();
return Compiler.class.getClassLoader();
}
static public InputStream resourceAsStream(ClassLoader loader, String name){
if (loader == null) {
return ClassLoader.getSystemResourceAsStream(name);
} else {
return loader.getResourceAsStream(name);
}
}
static public URL getResource(ClassLoader loader, String name) {
if (loader == null) {
return ClassLoader.getSystemResource(name);
} else {
return loader.getResource(name);
}
}
static public Class classForName(String name, boolean load, ClassLoader loader) {
try {
Class c = null;
if (!(loader instanceof DynamicClassLoader))
c = DynamicClassLoader.findInMemoryClass(name);
if (c != null)
return c;
return Class.forName(name, load, loader);
} catch (ClassNotFoundException e) {
throw Util.sneakyThrow(e);
}
}
static public Class classForName(String name) {
return classForName(name.replaceAll("-", "_"), true, baseLoader());
}
static public Class classForNameNonLoading(String name) {
return classForName(name.replaceAll("-", "_"), false, baseLoader());
}
static public Class loadClassForName(String name){
try {
classForNameNonLoading(name);
} catch (Exception e) {
if (e instanceof ClassNotFoundException)
return null;
else
throw Util.sneakyThrow(e);
}
return classForName(name);
}
static public float aget(float[] xs, int i) {
return xs[i];
}
static public float aset(float[] xs, int i, float v) {
xs[i] = v;
return v;
}
static public int alength(float[] xs) {
return xs.length;
}
static public float[] aclone(float[] xs) {
return xs.clone();
}
static public double aget(double[] xs, int i) {
return xs[i];
}
static public double aset(double[] xs, int i, double v) {
xs[i] = v;
return v;
}
static public int alength(double[] xs) {
return xs.length;
}
static public double[] aclone(double[] xs) {
return xs.clone();
}
static public int aget(int[] xs, int i) {
return xs[i];
}
static public int aset(int[] xs, int i, int v) {
xs[i] = v;
return v;
}
static public int alength(int[] xs) {
return xs.length;
}
static public int[] aclone(int[] xs) {
return xs.clone();
}
static public long aget(long[] xs, int i) {
return xs[i];
}
static public long aset(long[] xs, int i, long v) {
xs[i] = v;
return v;
}
static public int alength(long[] xs) {
return xs.length;
}
static public long[] aclone(long[] xs) {
return xs.clone();
}
static public char aget(char[] xs, int i) {
return xs[i];
}
static public char aset(char[] xs, int i, char v) {
xs[i] = v;
return v;
}
static public int alength(char[] xs) {
return xs.length;
}
static public char[] aclone(char[] xs) {
return xs.clone();
}
static public byte aget(byte[] xs, int i) {
return xs[i];
}
static public byte aset(byte[] xs, int i, byte v) {
xs[i] = v;
return v;
}
static public int alength(byte[] xs) {
return xs.length;
}
static public byte[] aclone(byte[] xs) {
return xs.clone();
}
static public short aget(short[] xs, int i) {
return xs[i];
}
static public short aset(short[] xs, int i, short v) {
xs[i] = v;
return v;
}
static public int alength(short[] xs) {
return xs.length;
}
static public short[] aclone(short[] xs) {
return xs.clone();
}
static public boolean aget(boolean[] xs, int i) {
return xs[i];
}
static public boolean aset(boolean[] xs, int i, boolean v) {
xs[i] = v;
return v;
}
static public int alength(boolean[] xs) {
return xs.length;
}
static public boolean[] aclone(boolean[] xs) {
return xs.clone();
}
static public Object aget(Object[] xs, int i) {
return xs[i];
}
static public Object aset(Object[] xs, int i, Object v) {
xs[i] = v;
return v;
}
static public int alength(Object[] xs) {
return xs.length;
}
static public Object[] aclone(Object[] xs) {
return xs.clone();
}
public static native Object NSClassFromString(String s) /*-[
return NSClassFromString(s);
]-*/;
}
================================================
FILE: src/jvm/clojure/lang/Range.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.*;
/**
* Implements generic numeric (potentially infinite) range.
*/
public class Range extends ASeq implements IChunkedSeq, IReduce {
private static final int CHUNK_SIZE = 32;
// Invariants guarantee this is never an "empty" seq
// assert(start != end && step != 0)
final Object end;
final Object start;
final Object step;
final BoundsCheck boundsCheck;
private volatile IChunk _chunk; // lazy
private volatile ISeq _chunkNext; // lazy
private volatile ISeq _next; // cached
private static interface BoundsCheck extends Serializable {
boolean exceededBounds(Object val);
}
private static BoundsCheck positiveStep(final Object end) {
return new BoundsCheck() {
public boolean exceededBounds(Object val){
return Numbers.gte(val, end);
}
};
}
private static BoundsCheck negativeStep(final Object end) {
return new BoundsCheck() {
public boolean exceededBounds(Object val){
return Numbers.lte(val, end);
}
};
}
private Range(Object start, Object end, Object step, BoundsCheck boundsCheck){
this.end = end;
this.start = start;
this.step = step;
this.boundsCheck = boundsCheck;
}
private Range(Object start, Object end, Object step, BoundsCheck boundsCheck, IChunk chunk, ISeq chunkNext){
this.end = end;
this.start = start;
this.step = step;
this.boundsCheck = boundsCheck;
this._chunk = chunk;
this._chunkNext = chunkNext;
}
private Range(IPersistentMap meta, Object start, Object end, Object step, BoundsCheck boundsCheck, IChunk chunk, ISeq chunkNext){
super(meta);
this.end = end;
this.start = start;
this.step = step;
this.boundsCheck = boundsCheck;
this._chunk = chunk;
this._chunkNext = chunkNext;
}
public static ISeq create(Object end) {
if(Numbers.isPos(end))
return new Range(0L, end, 1L, positiveStep(end));
return PersistentList.EMPTY;
}
public static ISeq create(Object start, Object end) {
return create(start, end, 1L);
}
public static ISeq create(final Object start, Object end, Object step) {
if((Numbers.isPos(step) && Numbers.gt(start, end)) ||
(Numbers.isNeg(step) && Numbers.gt(end, start)) ||
Numbers.equiv(start, end))
return PersistentList.EMPTY;
if(Numbers.isZero(step))
return Repeat.create(start);
return new Range(start, end, step, Numbers.isPos(step)?positiveStep(end):negativeStep(end));
}
public Obj withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
return new Range(meta, end, start, step, boundsCheck, _chunk, _chunkNext);
}
public Object first(){
return start;
}
public void forceChunk() {
if(_chunk != null) return;
Object[] arr = new Object[CHUNK_SIZE];
int n = 0;
Object val = start;
while(n < CHUNK_SIZE) {
arr[n++] = val;
val = Numbers.addP(val, step);
if(boundsCheck.exceededBounds(val)) {
//partial last chunk
_chunk = new ArrayChunk(arr, 0, n);
return;
}
}
// full last chunk
if(boundsCheck.exceededBounds(val)) {
_chunk = new ArrayChunk(arr, 0, CHUNK_SIZE);
return;
}
// full intermediate chunk
_chunk = new ArrayChunk(arr, 0, CHUNK_SIZE);
_chunkNext = new Range(val, end, step, boundsCheck);
}
public ISeq next() {
if(_next != null)
return _next;
forceChunk();
if(_chunk.count() > 1) {
IChunk smallerChunk = _chunk.dropFirst();
_next = new Range(smallerChunk.nth(0), end, step, boundsCheck, smallerChunk, _chunkNext);
return _next;
}
return chunkedNext();
}
public IChunk chunkedFirst() {
forceChunk();
return _chunk;
}
public ISeq chunkedNext() {
return chunkedMore().seq();
}
public ISeq chunkedMore() {
forceChunk();
if(_chunkNext == null)
return PersistentList.EMPTY;
return _chunkNext;
}
public Object reduce(IFn f) {
Object acc = start;
Number i = Numbers.addP(start, step);
while(! boundsCheck.exceededBounds(i)) {
acc = f.invoke(acc, i);
if (RT.isReduced(acc)) return ((Reduced)acc).deref();
i = Numbers.addP(i, step);
}
return acc;
}
public Object reduce(IFn f, Object val) {
Object acc = val;
Object i = start;
while(! boundsCheck.exceededBounds(i)) {
acc = f.invoke(acc, i);
if (RT.isReduced(acc)) return ((Reduced)acc).deref();
i = Numbers.addP(i, step);
}
return acc;
}
public Iterator iterator() {
return new RangeIterator();
}
private class RangeIterator implements Iterator {
private Object next;
public RangeIterator() {
this.next = start;
}
public boolean hasNext() {
return(! boundsCheck.exceededBounds(next));
}
public Object next() {
if (hasNext()) {
Object ret = next;
next = Numbers.addP(next, step);
return ret;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
================================================
FILE: src/jvm/clojure/lang/Ratio.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 31, 2008 */
package clojure.lang;
import java.math.BigInteger;
import java.math.BigDecimal;
import java.math.MathContext;
public class Ratio extends Number implements Comparable{
final public BigInteger numerator;
final public BigInteger denominator;
public Ratio(BigInteger numerator, BigInteger denominator){
this.numerator = numerator;
this.denominator = denominator;
}
public boolean equals(Object arg0){
return arg0 != null
&& arg0 instanceof Ratio
&& ((Ratio) arg0).numerator.equals(numerator)
&& ((Ratio) arg0).denominator.equals(denominator);
}
public int hashCode(){
return Util.hash(numerator) ^ Util.hash(denominator);
}
public String toString(){
return numerator.toString() + "/" + denominator.toString();
}
public int intValue(){
return (int) doubleValue();
}
public long longValue(){
return bigIntegerValue().longValue();
}
public float floatValue(){
return (float)doubleValue();
}
public double doubleValue(){
return decimalValue(MathContext.DECIMAL64).doubleValue();
}
public BigDecimal decimalValue(){
return decimalValue(MathContext.UNLIMITED);
}
public BigDecimal decimalValue(MathContext mc){
BigDecimal numerator = new BigDecimal(this.numerator);
BigDecimal denominator = new BigDecimal(this.denominator);
return numerator.divide(denominator, mc);
}
public BigInteger bigIntegerValue(){
return numerator.divide(denominator);
}
public int compareTo(Object o){
Number other = (Number)o;
return Numbers.compare(this, other);
}
}
================================================
FILE: src/jvm/clojure/lang/ReaderConditional.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public class ReaderConditional implements ILookup {
public static final Keyword FORM_KW = Keyword.intern("form");
public static final Keyword SPLICING_KW = Keyword.intern("splicing?");
public final Object form;
public final Boolean splicing;
public static ReaderConditional create(Object form, boolean splicing) {
return new ReaderConditional(form, splicing);
}
private ReaderConditional(Object form, boolean splicing){
this.form = form;
this.splicing = splicing;
}
public Object valAt(Object key) {
return valAt(key, null);
}
public Object valAt(Object key, Object notFound) {
if (FORM_KW.equals(key)) {
return this.form;
} else if (SPLICING_KW.equals(key)) {
return this.splicing;
} else {
return notFound;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReaderConditional that = (ReaderConditional) o;
if (form != null ? !form.equals(that.form) : that.form != null) return false;
if (splicing != null ? !splicing.equals(that.splicing) : that.splicing != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = Util.hash(form);
result = 31 * result + Util.hash(splicing);
return result;
}
}
================================================
FILE: src/jvm/clojure/lang/RecordIterator.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
/* ghadi shayban Sep 24, 2014 */
package clojure.lang;
import java.util.Iterator;
public final class RecordIterator implements Iterator {
int i = 0;
final int basecnt;
final ILookup rec;
final IPersistentVector basefields;
final Iterator extmap;
public RecordIterator (ILookup rec, IPersistentVector basefields, Iterator extmap) {
this.rec = rec;
this.basefields = basefields;
this.basecnt = basefields.count();
this.extmap = extmap;
}
public boolean hasNext() {
if (i < basecnt) {
return true;
} else {
return extmap.hasNext();
}
}
public Object next() {
if (i < basecnt) {
Object k = basefields.nth(i);
i++;
return new MapEntry(k, rec.valAt(k));
} else {
return extmap.next();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
================================================
FILE: src/jvm/clojure/lang/Reduced.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public final class Reduced implements IDeref{
Object val;
public Reduced(Object val){
this.val = val;
}
public Object deref(){
return val;
}
}
================================================
FILE: src/jvm/clojure/lang/Ref.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 25, 2007 */
package clojure.lang;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Ref extends ARef implements IFn, Comparable, IRef{
public int compareTo(Ref ref) {
if(this.id == ref.id)
return 0;
else if(this.id < ref.id)
return -1;
else
return 1;
}
public int getMinHistory(){
return minHistory;
}
public Ref setMinHistory(int minHistory){
this.minHistory = minHistory;
return this;
}
public int getMaxHistory(){
return maxHistory;
}
public Ref setMaxHistory(int maxHistory){
this.maxHistory = maxHistory;
return this;
}
public static class TVal{
Object val;
long point;
TVal prior;
TVal next;
TVal(Object val, long point, TVal prior){
this.val = val;
this.point = point;
this.prior = prior;
this.next = prior.next;
this.prior.next = this;
this.next.prior = this;
}
TVal(Object val, long point){
this.val = val;
this.point = point;
this.next = this;
this.prior = this;
}
}
TVal tvals;
final AtomicInteger faults;
final ReentrantReadWriteLock lock;
LockingTransaction.Info tinfo;
//IFn validator;
final long id;
volatile int minHistory = 0;
volatile int maxHistory = 10;
static final AtomicLong ids = new AtomicLong();
public Ref(Object initVal) {
this(initVal, null);
}
public Ref(Object initVal,IPersistentMap meta) {
super(meta);
this.id = ids.getAndIncrement();
this.faults = new AtomicInteger();
this.lock = new ReentrantReadWriteLock();
tvals = new TVal(initVal, 0);
}
//the latest val
// ok out of transaction
Object currentVal(){
try
{
lock.readLock().lock();
if(tvals != null)
return tvals.val;
throw new IllegalStateException(this.toString() + " is unbound.");
}
finally
{
lock.readLock().unlock();
}
}
//*
public Object deref(){
LockingTransaction t = LockingTransaction.getRunning();
if(t == null)
return currentVal();
return t.doGet(this);
}
//void validate(IFn vf, Object val){
// try{
// if(vf != null && !RT.booleanCast(vf.invoke(val)))
// throw new IllegalStateException("Invalid ref state");
// }
// catch(RuntimeException re)
// {
// throw re;
// }
// catch(Exception e)
// {
// throw new IllegalStateException("Invalid ref state", e);
// }
//}
//
//public void setValidator(IFn vf){
// try
// {
// lock.writeLock().lock();
// validate(vf,currentVal());
// validator = vf;
// }
// finally
// {
// lock.writeLock().unlock();
// }
//}
//
//public IFn getValidator(){
// try
// {
// lock.readLock().lock();
// return validator;
// }
// finally
// {
// lock.readLock().unlock();
// }
//}
public Object set(Object val){
return LockingTransaction.getEx().doSet(this, val);
}
public Object commute(IFn fn, ISeq args) {
return LockingTransaction.getEx().doCommute(this, fn, args);
}
public Object alter(IFn fn, ISeq args) {
LockingTransaction t = LockingTransaction.getEx();
return t.doSet(this, fn.applyTo(RT.cons(t.doGet(this), args)));
}
public void touch(){
LockingTransaction.getEx().doEnsure(this);
}
//*/
boolean isBound(){
try
{
lock.readLock().lock();
return tvals != null;
}
finally
{
lock.readLock().unlock();
}
}
public void trimHistory(){
try
{
lock.writeLock().lock();
if(tvals != null)
{
tvals.next = tvals;
tvals.prior = tvals;
}
}
finally
{
lock.writeLock().unlock();
}
}
public int getHistoryCount(){
try
{
lock.writeLock().lock();
return histCount();
}
finally
{
lock.writeLock().unlock();
}
}
int histCount(){
if(tvals == null)
return 0;
else
{
int count = 0;
for(TVal tv = tvals.next;tv != tvals;tv = tv.next)
count++;
return count;
}
}
final public IFn fn(){
return (IFn) deref();
}
public Object call() {
return invoke();
}
public void run(){
invoke();
}
public Object invoke() {
return fn().invoke();
}
public Object invoke(Object arg1) {
return fn().invoke(arg1);
}
public Object invoke(Object arg1, Object arg2) {
return fn().invoke(arg1, arg2);
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
return fn().invoke(arg1, arg2, arg3);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
return fn().invoke(arg1, arg2, arg3, arg4);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
{
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
{
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
{
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
arg16);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
arg16, arg17);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
arg16, arg17, arg18);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
arg16, arg17, arg18, arg19);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
{
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
arg16, arg17, arg18, arg19, arg20);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
Object... args)
{
return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
arg16, arg17, arg18, arg19, arg20, args);
}
public Object applyTo(ISeq arglist) {
return AFn.applyToHelper(this, arglist);
}
}
================================================
FILE: src/jvm/clojure/lang/Reflector.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Apr 19, 2006 */
package clojure.lang;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Reflector{
public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) {
Class c = target.getClass();
List methods = getMethods(c, args.length, methodName, false);
return invokeMatchingMethod(methodName, methods, target, args);
}
private static Throwable getCauseOrElse(Exception e) {
if (e.getCause() != null)
return e.getCause();
return e;
}
private static RuntimeException throwCauseOrElseException(Exception e) {
if (e.getCause() != null)
throw Util.sneakyThrow(e.getCause());
throw Util.sneakyThrow(e);
}
private static String noMethodReport(String methodName, Object target){
return "No matching method found: " + methodName
+ (target==null?"":" for " + target.getClass());
}
static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args)
{
Method m = null;
Object[] boxedArgs = null;
if(methods.isEmpty())
{
throw new IllegalArgumentException(noMethodReport(methodName,target));
}
else if(methods.size() == 1)
{
m = (Method) methods.get(0);
boxedArgs = boxArgs(m.getParameterTypes(), args);
}
else //overloaded w/same arity
{
Method foundm = null;
for(Iterator i = methods.iterator(); i.hasNext();)
{
m = (Method) i.next();
Class[] params = m.getParameterTypes();
if(isCongruent(params, args))
{
if(foundm == null || Compiler.subsumes(params, foundm.getParameterTypes()))
{
foundm = m;
boxedArgs = boxArgs(params, args);
}
}
}
m = foundm;
}
if(m == null)
throw new IllegalArgumentException(noMethodReport(methodName,target));
if(!Modifier.isPublic(m.getDeclaringClass().getModifiers()))
{
//public method of non-public class, try to find it in hierarchy
Method oldm = m;
m = getAsMethodOfPublicBase(m.getDeclaringClass(), m);
if(m == null)
throw new IllegalArgumentException("Can't call public method of non-public class: " +
oldm.toString());
}
try
{
return prepRet(m.getReturnType(), m.invoke(target, boxedArgs));
}
catch(Exception e)
{
throw Util.sneakyThrow(getCauseOrElse(e));
}
}
public static Method getAsMethodOfPublicBase(Class c, Method m){
for(Class iface : c.getInterfaces())
{
for(Method im : iface.getMethods())
{
if(isMatch(im, m))
{
return im;
}
}
}
Class sc = c.getSuperclass();
if(sc == null)
return null;
for(Method scm : sc.getMethods())
{
if(isMatch(scm, m))
{
return scm;
}
}
return getAsMethodOfPublicBase(sc, m);
}
public static boolean isMatch(Method lhs, Method rhs) {
if(!lhs.getName().equals(rhs.getName())
|| !Modifier.isPublic(lhs.getDeclaringClass().getModifiers()))
{
return false;
}
Class[] types1 = lhs.getParameterTypes();
Class[] types2 = rhs.getParameterTypes();
if(types1.length != types2.length)
return false;
boolean match = true;
for (int i=0; i 0)
return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY);
else
return getInstanceField(target, name);
}
}
public static Object invokeInstanceMember(Object target, String name) {
//check for field first
Class c = target.getClass();
Field f = getField(c, name, false);
if(f != null) //field get
{
try
{
return prepRet(f.getType(), f.get(target));
}
catch(IllegalAccessException e)
{
throw Util.sneakyThrow(e);
}
}
return invokeInstanceMethod(target, name, RT.EMPTY_ARRAY);
}
public static Object invokeInstanceMember(String name, Object target, Object arg1) {
//check for field first
Class c = target.getClass();
Field f = getField(c, name, false);
if(f != null) //field set
{
try
{
f.set(target, boxArg(f.getType(), arg1));
}
catch(IllegalAccessException e)
{
throw Util.sneakyThrow(e);
}
return arg1;
}
return invokeInstanceMethod(target, name, new Object[]{arg1});
}
public static Object invokeInstanceMember(String name, Object target, Object... args) {
return invokeInstanceMethod(target, name, args);
}
static public Field getField(Class c, String name, boolean getStatics){
Field[] allfields = c.getFields();
for(int i = 0; i < allfields.length; i++)
{
if(name.equals(allfields[i].getName())
&& Modifier.isStatic(allfields[i].getModifiers()) == getStatics)
return allfields[i];
}
return null;
}
static public List getMethods(Class c, int arity, String name, boolean getStatics){
Method[] allmethods = c.getMethods();
ArrayList methods = new ArrayList();
ArrayList bridgeMethods = new ArrayList();
for(int i = 0; i < allmethods.length; i++)
{
Method method = allmethods[i];
if(name.equals(method.getName())
&& Modifier.isStatic(method.getModifiers()) == getStatics
&& method.getParameterTypes().length == arity)
{
try
{
if(method.isBridge()
&& c.getMethod(method.getName(), method.getParameterTypes())
.equals(method))
bridgeMethods.add(method);
else
methods.add(method);
}
catch(NoSuchMethodException e)
{
}
}
// && (!method.isBridge()
// || (c == StringBuilder.class &&
// c.getMethod(method.getName(), method.getParameterTypes())
// .equals(method))))
// {
// methods.add(allmethods[i]);
// }
}
if(methods.isEmpty())
methods.addAll(bridgeMethods);
if(!getStatics && c.isInterface())
{
allmethods = Object.class.getMethods();
for(int i = 0; i < allmethods.length; i++)
{
if(name.equals(allmethods[i].getName())
&& Modifier.isStatic(allmethods[i].getModifiers()) == getStatics
&& allmethods[i].getParameterTypes().length == arity)
{
methods.add(allmethods[i]);
}
}
}
return methods;
}
static Object boxArg(Class paramType, Object arg){
if(!paramType.isPrimitive())
return paramType.cast(arg);
else if(paramType == boolean.class)
return Boolean.class.cast(arg);
else if(paramType == char.class)
return Character.class.cast(arg);
else if(arg instanceof Number)
{
Number n = (Number) arg;
if(paramType == int.class)
return n.intValue();
else if(paramType == float.class)
return n.floatValue();
else if(paramType == double.class)
return n.doubleValue();
else if(paramType == long.class)
return n.longValue();
else if(paramType == short.class)
return n.shortValue();
else if(paramType == byte.class)
return n.byteValue();
}
throw new IllegalArgumentException("Unexpected param type, expected: " + paramType +
", given: " + arg.getClass().getName());
}
static Object[] boxArgs(Class[] params, Object[] args){
if(params.length == 0)
return null;
Object[] ret = new Object[params.length];
for(int i = 0; i < params.length; i++)
{
Object arg = args[i];
Class paramType = params[i];
ret[i] = boxArg(paramType, arg);
}
return ret;
}
static public boolean paramArgTypeMatch(Class paramType, Class argType){
if(argType == null)
return !paramType.isPrimitive();
if(paramType == argType || paramType.isAssignableFrom(argType))
return true;
if(paramType == int.class)
return argType == Integer.class
|| argType == long.class
|| argType == Long.class
|| argType == short.class
|| argType == byte.class;// || argType == FixNum.class;
else if(paramType == float.class)
return argType == Float.class
|| argType == double.class;
else if(paramType == double.class)
return argType == Double.class
|| argType == float.class;// || argType == DoubleNum.class;
else if(paramType == long.class)
return argType == Long.class
|| argType == int.class
|| argType == short.class
|| argType == byte.class;// || argType == BigNum.class;
else if(paramType == char.class)
return argType == Character.class;
else if(paramType == short.class)
return argType == Short.class;
else if(paramType == byte.class)
return argType == Byte.class;
else if(paramType == boolean.class)
return argType == Boolean.class;
return false;
}
static boolean isCongruent(Class[] params, Object[] args){
boolean ret = false;
if(args == null)
return params.length == 0;
if(params.length == args.length)
{
ret = true;
for(int i = 0; ret && i < params.length; i++)
{
Object arg = args[i];
Class argType = (arg == null) ? null : arg.getClass();
Class paramType = params[i];
ret = paramArgTypeMatch(paramType, argType);
}
}
return ret;
}
public static Object prepRet(Class c, Object x){
if (!(c.isPrimitive() || c == Boolean.class))
return x;
if(x instanceof Boolean)
return ((Boolean) x)?Boolean.TRUE:Boolean.FALSE;
// else if(x instanceof Integer)
// {
// return ((Integer)x).longValue();
// }
// else if(x instanceof Float)
// return Double.valueOf(((Float) x).doubleValue());
return x;
}
}
================================================
FILE: src/jvm/clojure/lang/RemoteRef.java
================================================
package clojure.lang;
/*-[
#include "clojure/core_gensym.h"
]-*/
public class RemoteRef extends RestFn {
private static Object nsPlaceholderString = new Object();
private static final String OBJC_REF = "objc-ref-";
private static final String JVM_REF = "jvm-ref-";
private static Atom a = new Atom(RT.map());
private static Atom i = new Atom(RT.map());
private static Var gensym = RT.var("clojure.core", "gensym");
public static void reset() {
a.reset(RT.map());
i.reset(RT.map());
}
static {
register(nsPlaceholderString);
}
public static Object register(final Object o) {
if (ObjC.objc && classDescription(o).equals("NSPlaceholderString")) {
nativeRelease(o);
return register(nsPlaceholderString);
}
IPersistentMap lookup = (IPersistentMap) i.deref();
Object curr = lookup.valAt(o);
if (curr == null) {
Object invoke = ObjC.objc ? nativeGensym(OBJC_REF) : ((AFn) gensym
.getRawRoot()).invoke(JVM_REF);
final String id = ((Symbol) invoke).getName();
a.swap(new AFn() {
@Override
public Object invoke(Object old) {
return RT.assoc(old, id, o);
}
});
i.swap(new AFn() {
@Override
public Object invoke(Object old) {
return RT.assoc(old, o, id);
}
});
return id;
} else {
return curr;
}
}
private static native Object classDescription(Object o) /*-[
return [[o class] description];
]-*/;
private static native void nativeRelease(Object o) /*-[
[o release];
]-*/;
private static native void nativeRetain(Object o) /*-[
[o retain];
]-*/;
private native static Object nativeGensym(String objcRef) /*-[
return [Clojurecore_gensym_get_VAR_() invokeWithId:objcRef];
]-*/;
private String id;
public RemoteRef(String id) {
this.id = id;
}
public String getId() {
return id;
}
public Object get() {
if (ObjC.objc) {
if (id.startsWith(JVM_REF)) {
nativeRetain(this);
return this;
} else {
Object o = (Object) RT.get(a.deref(), id);
if (o.equals(nsPlaceholderString)) {
return allocNativeString();
} else {
return o;
}
}
} else {
if (id.startsWith(OBJC_REF)) {
return this;
} else {
return (Object) RT.get(a.deref(), id);
}
}
}
private static native Object allocNativeString() /*-[
return [NSString alloc]; // leaks in repl
]-*/;
@Override
protected Object doInvoke(Object args) {
return RemoteRepl.callRemote(this, args);
}
@Override
public int getRequiredArity() {
return 0;
}
@Override
public boolean equals(Object f) {
if (f != null && f instanceof RemoteRef) {
return ((RemoteRef) f).id.equals(id);
}
return false;
}
}
================================================
FILE: src/jvm/clojure/lang/RemoteRepl.java
================================================
package clojure.lang;
import java.io.IOException;
/*-[
#include "ReplClient.h"
]-*/
public class RemoteRepl {
public static boolean connected;
static Var callRemoteSel = RT.var("clojure.remoterepl", "call-remote");
static Var listen = RT.var("clojure.remoterepl", "listen");
public static Object callRemote(Object o, Object seq) {
if (RemoteRepl.connected) {
if (ObjC.objc) {
return callRemoteSelNative(o, RT.seq(seq));
} else {
if (o == null) {
throw new NullPointerException();
}
return callRemoteSel.invoke(o, RT.seq(seq));
}
} else {
//throw new RuntimeException("RemoteRepl not connected");
return null;
}
}
private native static Object callRemoteSelNative(Object o, ISeq seq) /*-[
return [ReplClient callRemote:o args:seq];
]-*/;
public static void setConnected(boolean connected) {
RemoteRepl.connected = connected;
RemoteRef.reset();
}
public static void listen() {
try {
RT.load("clojure/remoterepl");
listen.invoke();
} catch (Exception e) {
throw Util.sneakyThrow(e);
}
}
public native static void connect(String host, String port) /*-[
[ReplClient connect:host];
]-*/;
public static native Object safetry(AFn fn) /*-[
@try {
return [fn invoke];
}
@catch (NSException *exception) {
NSLog(@"%@", [exception callStackSymbols]);
return nil;
}
]-*/;
}
================================================
FILE: src/jvm/clojure/lang/Repeat.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
/* Alex Miller, Dec 5, 2014 */
public class Repeat extends ASeq implements IReduce {
private static final long INFINITE = -1;
private final long count; // always INFINITE or >0
private final Object val;
private volatile ISeq _next; // cached
private Repeat(long count, Object val){
this.count = count;
this.val = val;
}
private Repeat(IPersistentMap meta, long count, Object val){
super(meta);
this.count = count;
this.val = val;
}
public static Repeat create(Object val){
return new Repeat(INFINITE, val);
}
public static ISeq create(long count, Object val){
if(count <= 0)
return PersistentList.EMPTY;
return new Repeat(count, val);
}
public Object first(){
return val;
}
public ISeq next() {
if(_next == null) {
if(count > 1)
_next = new Repeat(count - 1, val);
else if(count == INFINITE)
_next = this;
}
return _next;
}
public Repeat withMeta(IPersistentMap meta){
return new Repeat(meta, count, val);
}
public Object reduce(IFn f){
Object ret = val;
if(count == INFINITE) {
while(true){
ret = f.invoke(ret, val);
if(RT.isReduced(ret))
return ((IDeref)ret).deref();
}
} else {
for(long i=1; i= 0; --i)
ret = RT.cons(args[i], ret);
return ret;
}
protected static ISeq findKey(Object key, ISeq args){
while(args != null)
{
if(key == args.first())
return args.next();
args = RT.next(args);
args = RT.next(args);
}
return null;
}
}
================================================
FILE: src/jvm/clojure/lang/RestFnWithMeta.java
================================================
package clojure.lang;
public class RestFnWithMeta extends RestFn {
final AFunction aFunction;
final IPersistentMap meta;
public RestFnWithMeta(AFunction aFunction, IPersistentMap meta) {
this.aFunction = aFunction;
this.meta = meta;
}
protected Object doInvoke(Object args) {
return aFunction.applyTo((ISeq) args);
}
public IPersistentMap meta() {
return meta;
}
public IObj withMeta(IPersistentMap meta) {
return aFunction.withMeta(meta);
}
public int getRequiredArity() {
return 0;
}
}
================================================
FILE: src/jvm/clojure/lang/Reversible.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 5, 2008 */
package clojure.lang;
public interface Reversible{
ISeq rseq() ;
}
================================================
FILE: src/jvm/clojure/lang/Selector.java
================================================
package clojure.lang;
/*-[
#import "NSCommon.h"
]-*/
public class Selector extends RestFn implements Named {
public static Object invokeSelector(String sel, Object o) {
return invokeSelector(sel, o, null);
}
public static Object invokeSelector(String sel, Object o, Object args) {
if (!ObjC.objc) {
return RemoteRepl.callRemote(new Selector(sel), RT.cons(o, args));
} else {
if (o == null) {
throw new NullPointerException();
}
if (args != null && !sel.endsWith(":")) {
sel = sel + ":";
}
return invokeSel(o, sel, RT.seq(args));
}
}
public final String sel;
public Selector(Symbol sel) {
this.sel = sel.name;
}
public Selector(String sel) {
this.sel = sel;
}
@Override
public String getNamespace() {
return null;
}
@Override
public String getName() {
return sel;
}
@Override
protected Object doInvoke(Object o, Object args) {
return invokeSelector(sel, o, args);
}
public static native Object invokeSel(Object object, String selector,
ISeq arguments) /*-[
return [NSCommon invokeSel:object withSelector:selector withArgs:arguments];
]-*/;
@Override
public int getRequiredArity() {
return 1;
}
@Override
public String toString() {
return sel;
}
}
================================================
FILE: src/jvm/clojure/lang/SeqEnumeration.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
import java.util.Enumeration;
public class SeqEnumeration implements Enumeration{
ISeq seq;
public SeqEnumeration(ISeq seq){
this.seq = seq;
}
public boolean hasMoreElements(){
return seq != null;
}
public Object nextElement(){
Object ret = RT.first(seq);
seq = RT.next(seq);
return ret;
}
}
================================================
FILE: src/jvm/clojure/lang/SeqIterator.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jun 19, 2007 */
package clojure.lang;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class SeqIterator implements Iterator{
static final Object START = new Object();
Object seq;
Object next;
public SeqIterator(Object o){
seq = START;
next = o;
}
//preserved for binary compatibility
public SeqIterator(ISeq o){
seq = START;
next = o;
}
public boolean hasNext(){
if(seq == START){
seq = null;
next = RT.seq(next);
}
else if(seq == next)
next = RT.next(seq);
return next != null;
}
public Object next() throws NoSuchElementException {
if(!hasNext())
throw new NoSuchElementException();
seq = next;
return RT.first(next);
}
public void remove(){
throw new UnsupportedOperationException();
}
}
================================================
FILE: src/jvm/clojure/lang/Seqable.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 28, 2009 */
package clojure.lang;
public interface Seqable {
ISeq seq();
}
================================================
FILE: src/jvm/clojure/lang/Sequential.java
================================================
package clojure.lang;
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
*/
public interface Sequential {
}
================================================
FILE: src/jvm/clojure/lang/Settable.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 31, 2008 */
package clojure.lang;
public interface Settable {
Object doSet(Object val) ;
Object doReset(Object val) ;
}
================================================
FILE: src/jvm/clojure/lang/Sorted.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Apr 15, 2008 */
package clojure.lang;
import java.util.Comparator;
public interface Sorted{
Comparator comparator();
Object entryKey(Object entry);
ISeq seq(boolean ascending);
ISeq seqFrom(Object key, boolean ascending);
}
================================================
FILE: src/jvm/clojure/lang/SourceGenIntrinsics.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich 9/5/11 */
package clojure.lang;
import clojure.asm.Opcodes;
public class SourceGenIntrinsics implements Opcodes{
static IPersistentMap ops = RT.map(
"public static double clojure.lang.Numbers.add(double,double)", "+",
"public static long clojure.lang.Numbers.and(long,long)", "&",
"public static long clojure.lang.Numbers.or(long,long)", "|",
"public static long clojure.lang.Numbers.xor(long,long)", "^",
"public static double clojure.lang.Numbers.multiply(double,double)", "*",
"public static double clojure.lang.Numbers.divide(double,double)", "/",
"public static long clojure.lang.Numbers.remainder(long,long)", "%",
"public static long clojure.lang.Numbers.shiftLeft(long,long)", "<<",
"public static long clojure.lang.Numbers.shiftRight(long,long)", ">>",
"public static long clojure.lang.Numbers.unsignedShiftRight(long,long)", ">>>",
"public static double clojure.lang.Numbers.minus(double)", "-",
"public static double clojure.lang.Numbers.minus(double,double)", "-",
"public static double clojure.lang.Numbers.inc(double)", "1+",
"public static double clojure.lang.Numbers.dec(double)", "(-1)+",
"public static long clojure.lang.Numbers.quotient(long,long)", "/",
"public static int clojure.lang.Numbers.shiftLeftInt(int,int)", "<<",
"public static int clojure.lang.Numbers.shiftRightInt(int,int)", ">>",
"public static int clojure.lang.Numbers.unsignedShiftRightInt(int,int)", ">>>",
"public static int clojure.lang.Numbers.unchecked_int_add(int,int)", "+",
"public static int clojure.lang.Numbers.unchecked_int_subtract(int,int)", "-",
"public static int clojure.lang.Numbers.unchecked_int_negate(int)", "-",
"public static int clojure.lang.Numbers.unchecked_int_inc(int)", "+",
"public static int clojure.lang.Numbers.unchecked_int_dec(int)", "-",
"public static int clojure.lang.Numbers.unchecked_int_multiply(int,int)", "*",
"public static int clojure.lang.Numbers.unchecked_int_divide(int,int)", "/",
"public static int clojure.lang.Numbers.unchecked_int_remainder(int,int)", "%",
"public static long clojure.lang.Numbers.unchecked_add(long,long)", "+",
"public static double clojure.lang.Numbers.unchecked_add(double,double)", "+",
"public static long clojure.lang.Numbers.unchecked_minus(long)", "-",
"public static double clojure.lang.Numbers.unchecked_minus(double)", "-",
"public static double clojure.lang.Numbers.unchecked_minus(double,double)", "-",
"public static long clojure.lang.Numbers.unchecked_minus(long,long)", "-",
"public static long clojure.lang.Numbers.unchecked_multiply(long,long)", "*",
"public static double clojure.lang.Numbers.unchecked_multiply(double,double)", "*",
"public static double clojure.lang.Numbers.unchecked_inc(double)", "1+",
"public static long clojure.lang.Numbers.unchecked_inc(long)", "1+",
"public static double clojure.lang.Numbers.unchecked_dec(double)", "(-1)+",
"public static long clojure.lang.Numbers.unchecked_dec(long)", "(-1)+",
"public static short clojure.lang.RT.aget(short[],int)", "aget[]",
"public static float clojure.lang.RT.aget(float[],int)", "aget[]",
"public static double clojure.lang.RT.aget(double[],int)", "aget[]",
"public static int clojure.lang.RT.aget(int[],int)", "aget[]",
"public static long clojure.lang.RT.aget(long[],int)", "aget[]",
"public static char clojure.lang.RT.aget(char[],int)", "aget[]",
"public static byte clojure.lang.RT.aget(byte[],int)", "aget[]",
"public static boolean clojure.lang.RT.aget(boolean[],int)", "aget[]",
"public static java.lang.Object clojure.lang.RT.aget(java.lang.Object[],int)", "aget[]",
"public static int clojure.lang.RT.alength(int[])", "alength[]",
"public static int clojure.lang.RT.alength(long[])", "alength[]",
"public static int clojure.lang.RT.alength(char[])", "alength[]",
"public static int clojure.lang.RT.alength(java.lang.Object[])", "alength[]",
"public static int clojure.lang.RT.alength(byte[])", "alength[]",
"public static int clojure.lang.RT.alength(float[])", "alength[]",
"public static int clojure.lang.RT.alength(short[])", "alength[]",
"public static int clojure.lang.RT.alength(boolean[])", "alength[]",
"public static int clojure.lang.RT.alength(double[])", "alength[]",
"public static double clojure.lang.RT.doubleCast(long)", "(double)",
"public static double clojure.lang.RT.doubleCast(double)", "",
"public static double clojure.lang.RT.doubleCast(float)", "(double)",
"public static double clojure.lang.RT.doubleCast(int)", "(double)",
"public static double clojure.lang.RT.doubleCast(short)", "(double)",
"public static double clojure.lang.RT.doubleCast(byte)", "(double)",
"public static double clojure.lang.RT.uncheckedDoubleCast(double)", "(double)",
"public static double clojure.lang.RT.uncheckedDoubleCast(float)", "(double)",
"public static double clojure.lang.RT.uncheckedDoubleCast(long)", "(double)",
"public static double clojure.lang.RT.uncheckedDoubleCast(int)", "(double)",
"public static double clojure.lang.RT.uncheckedDoubleCast(short)", "(double)",
"public static double clojure.lang.RT.uncheckedDoubleCast(byte)", "(double)",
"public static long clojure.lang.RT.longCast(long)", "",
"public static long clojure.lang.RT.longCast(short)", "(long)",
"public static long clojure.lang.RT.longCast(byte)", "(long)",
"public static long clojure.lang.RT.longCast(int)", "(long)",
"public static int clojure.lang.RT.uncheckedIntCast(long)", "(int)",
"public static int clojure.lang.RT.uncheckedIntCast(double)", "(int)",
"public static int clojure.lang.RT.uncheckedIntCast(byte)", "",
"public static int clojure.lang.RT.uncheckedIntCast(short)", "",
"public static int clojure.lang.RT.uncheckedIntCast(char)", "",
"public static int clojure.lang.RT.uncheckedIntCast(int)", "",
"public static int clojure.lang.RT.uncheckedIntCast(float)", "(int)",
"public static long clojure.lang.RT.uncheckedLongCast(short)", "(long)",
"public static long clojure.lang.RT.uncheckedLongCast(float)", "(long)",
"public static long clojure.lang.RT.uncheckedLongCast(double)", "(long)",
"public static long clojure.lang.RT.uncheckedLongCast(byte)", "(long)",
"public static long clojure.lang.RT.uncheckedLongCast(long)", "",
"public static long clojure.lang.RT.uncheckedLongCast(int)", "(long)"
);
//map to instructions terminated with comparator for branch to false
static IPersistentMap preds = RT.map(
"public static boolean clojure.lang.Numbers.lt(double,double)", "<",
"public static boolean clojure.lang.Numbers.lt(long,long)", "<",
"public static boolean clojure.lang.Numbers.equiv(double,double)", "==",
"public static boolean clojure.lang.Numbers.equiv(long,long)", "==",
"public static boolean clojure.lang.Numbers.lte(double,double)", "<=",
"public static boolean clojure.lang.Numbers.lte(long,long)", "<=",
"public static boolean clojure.lang.Numbers.gt(long,long)", ">",
"public static boolean clojure.lang.Numbers.gt(double,double)", ">",
"public static boolean clojure.lang.Numbers.gte(long,long)", ">=",
"public static boolean clojure.lang.Numbers.gte(double,double)", ">=",
"public static boolean clojure.lang.Util.equiv(long,long)", "==",
"public static boolean clojure.lang.Util.equiv(boolean,boolean)", "==",
"public static boolean clojure.lang.Util.equiv(double,double)", "==",
"public static boolean clojure.lang.Numbers.isZero(double)", "0.0 ==",
"public static boolean clojure.lang.Numbers.isZero(long)", "0L ==",
"public static boolean clojure.lang.Numbers.isPos(long)", "0 <",
"public static boolean clojure.lang.Numbers.isPos(double)", "0 <",
"public static boolean clojure.lang.Numbers.isNeg(long)", "0 >",
"public static boolean clojure.lang.Numbers.isNeg(double)", "0 >"
);
}
================================================
FILE: src/jvm/clojure/lang/SourceWriter.java
================================================
package clojure.lang;
public class SourceWriter {
private final StringBuilder sb = new StringBuilder();
private int tab = 0;
public void tab() {
tab++;
}
public void untab() {
tab --;
}
public void println(String l) {
if (tab > 0) {
sb.append(new String(new char[tab]).replace("\0", " "));
}
sb.append(l).append("\n");
}
@Override
public String toString() {
return sb.toString();
}
public void println() {
sb.append("\n");
}
}
================================================
FILE: src/jvm/clojure/lang/StringEscapeUtils.java
================================================
/*
* Copyright 2002-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package clojure.lang;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
/**
*
Escapes and unescapes Strings for
* Java, Java Script, HTML, XML, and SQL.
*
* @author Apache Jakarta Turbine
* @author GenerationJavaCore library
* @author Purple Technology
* @author Henri Yandell
* @author Alexander Day Chaffee
* @author Antony Riley
* @author Helge Tesgaard
* @author Sean Brown
* @author Gary Gregory
* @author Phil Steitz
* @author Pete Gieser
* @since 2.0
* @version $Id: StringEscapeUtils.java 165657 2005-05-02 18:31:49Z ggregory $
*/
public class StringEscapeUtils {
public static void main(String[] args) {
String str = "(\\d\\d\\d\\d)";
System.out.println(str);
System.out.println(escapeJava(str));
}
/**
*
StringEscapeUtils instances should NOT be constructed in
* standard programming.
*
*
Instead, the class should be used as:
*
StringEscapeUtils.escapeJava("foo");
*
*
This constructor is public to permit tools that require a JavaBean
* instance to operate.
*/
public StringEscapeUtils() {
}
// Java and JavaScript
//--------------------------------------------------------------------------
/**
*
Escapes the characters in a String using Java String rules.
*
*
Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.)
*
*
So a tab becomes the characters '\\' and
* 't'.
*
*
The only difference between Java strings and JavaScript strings
* is that in JavaScript, a single quote must be escaped.
*
*
Example:
*
* input string: He didn't say, "Stop!"
* output string: He didn't say, \"Stop!\"
*
*
*
* @param str String to escape values in, may be null
* @return String with escaped values, null if null string input
*/
public static String escapeJava(String str) {
return escapeJavaStyleString(str, false);
}
/**
*
Escapes the characters in a String using Java String rules to
* a Writer.
*
*
A null string input has no effect.
*
* @see #escapeJava(java.lang.String)
* @param out Writer to write escaped string into
* @param str String to escape values in, may be null
* @throws IllegalArgumentException if the Writer is null
* @throws IOException if error occurs on underlying Writer
*/
public static void escapeJava(Writer out, String str) throws IOException {
escapeJavaStyleString(out, str, false);
}
private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes) {
if (str == null) {
return null;
}
try {
StringWriter writer = new StringWriter(str.length() * 2);
escapeJavaStyleString(writer, str, escapeSingleQuotes);
return writer.toString();
} catch (IOException ioe) {
// this should never ever happen while writing to a StringWriter
ioe.printStackTrace();
return null;
}
}
private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote) throws IOException {
if (out == null) {
throw new IllegalArgumentException("The Writer must not be null");
}
if (str == null) {
return;
}
int sz;
sz = str.length();
for (int i = 0; i < sz; i++) {
char ch = str.charAt(i);
// handle unicode
if (ch > 0xfff) {
out.write("\\u" + hex(ch));
} else if (ch > 0xff) {
out.write("\\u0" + hex(ch));
} else if (ch > 0x7f) {
out.write("\\u00" + hex(ch));
} else if (ch < 32) {
switch (ch) {
case '\b':
out.write('\\');
out.write('b');
break;
case '\n':
out.write('\\');
out.write('n');
break;
case '\t':
out.write('\\');
out.write('t');
break;
case '\f':
out.write('\\');
out.write('f');
break;
case '\r':
out.write('\\');
out.write('r');
break;
default :
if (ch > 0xf) {
out.write("\\u00" + hex(ch));
} else {
out.write("\\u000" + hex(ch));
}
break;
}
} else {
switch (ch) {
case '\'':
if (escapeSingleQuote) {
out.write('\\');
}
out.write('\'');
break;
case '"':
out.write('\\');
out.write('"');
break;
case '\\':
out.write('\\');
out.write('\\');
break;
default :
out.write(ch);
break;
}
}
}
}
/**
*
Returns an upper case hexadecimal String for the given
* character.
*
* @param ch The character to convert.
* @return An upper case hexadecimal String
*/
private static String hex(char ch) {
return Integer.toHexString(ch).toUpperCase();
}
/**
*
Unescapes any Java literals found in the String.
* For example, it will turn a sequence of '\' and
* 'n' into a newline character, unless the '\'
* is preceded by another '\'.
*
* @param str the String to unescape, may be null
* @return a new unescaped String, null if null string input
*/
public static String unescapeJava(String str) {
if (str == null) {
return null;
}
try {
StringWriter writer = new StringWriter(str.length());
unescapeJava(writer, str);
return writer.toString();
} catch (IOException ioe) {
// this should never ever happen while writing to a StringWriter
ioe.printStackTrace();
return null;
}
}
/**
*
Unescapes any Java literals found in the String to a
* Writer.
*
*
For example, it will turn a sequence of '\' and
* 'n' into a newline character, unless the '\'
* is preceded by another '\'.
*
*
A null string input has no effect.
*
* @param out the Writer used to output unescaped characters
* @param str the String to unescape, may be null
* @throws IllegalArgumentException if the Writer is null
* @throws IOException if error occurs on underlying Writer
*/
public static void unescapeJava(Writer out, String str) throws IOException {
if (out == null) {
throw new IllegalArgumentException("The Writer must not be null");
}
if (str == null) {
return;
}
int sz = str.length();
StringBuffer unicode = new StringBuffer(4);
boolean hadSlash = false;
boolean inUnicode = false;
for (int i = 0; i < sz; i++) {
char ch = str.charAt(i);
if (inUnicode) {
// if in unicode, then we're reading unicode
// values in somehow
unicode.append(ch);
if (unicode.length() == 4) {
// unicode now contains the four hex digits
// which represents our unicode character
try {
int value = Integer.parseInt(unicode.toString(), 16);
out.write((char) value);
unicode.setLength(0);
inUnicode = false;
hadSlash = false;
} catch (NumberFormatException nfe) {
throw new RuntimeException("Unable to parse unicode value: " + unicode, nfe);
}
}
continue;
}
if (hadSlash) {
// handle an escaped value
hadSlash = false;
switch (ch) {
case '\\':
out.write('\\');
break;
case '\'':
out.write('\'');
break;
case '\"':
out.write('"');
break;
case 'r':
out.write('\r');
break;
case 'f':
out.write('\f');
break;
case 't':
out.write('\t');
break;
case 'n':
out.write('\n');
break;
case 'b':
out.write('\b');
break;
case 'u':
{
// uh-oh, we're in unicode country....
inUnicode = true;
break;
}
default :
out.write(ch);
break;
}
continue;
} else if (ch == '\\') {
hadSlash = true;
continue;
}
out.write(ch);
}
if (hadSlash) {
// then we're in the weird case of a \ at the end of the
// string, let's output it anyway.
out.write('\\');
}
}
}
================================================
FILE: src/jvm/clojure/lang/StringSeq.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 6, 2007 */
package clojure.lang;
public class StringSeq extends ASeq implements IndexedSeq{
public final CharSequence s;
public final int i;
static public StringSeq create(CharSequence s){
if(s.length() == 0)
return null;
return new StringSeq(null, s, 0);
}
StringSeq(IPersistentMap meta, CharSequence s, int i){
super(meta);
this.s = s;
this.i = i;
}
public Obj withMeta(IPersistentMap meta){
if(meta == meta())
return this;
return new StringSeq(meta, s, i);
}
public Object first(){
return Character.valueOf(s.charAt(i));
}
public ISeq next(){
if(i + 1 < s.length())
return new StringSeq(_meta, s, i + 1);
return null;
}
public int index(){
return i;
}
public int count(){
return s.length() - i;
}
}
================================================
FILE: src/jvm/clojure/lang/Symbol.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 11:42:47 AM */
package clojure.lang;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Symbol extends AFn implements IObj, Comparable, Named, Serializable, IHashEq{
final String ns;
final String name;
private int _hasheq;
final IPersistentMap _meta;
transient String _str;
public String toString(){
if(_str == null){
if(ns != null)
_str = (ns + "/" + name);
else
_str = name;
}
return _str;
}
public String getNamespace() {
return ns;
}
public String getName() {
return name;
}
// the create thunks preserve binary compatibility with code compiled
// against earlier version of Clojure and can be removed (at some point).
static public Symbol create(String ns, String name) {
return Symbol.intern(ns, name);
}
static public Symbol create(String nsname) {
return Symbol.intern(nsname);
}
static public Symbol intern(String ns, String name){
return new Symbol(ns, name);
}
static public Symbol intern(String nsname){
int i = nsname.indexOf('/');
if(i == -1 || nsname.equals("/"))
return new Symbol(null, nsname);
else
return new Symbol(nsname.substring(0, i), nsname.substring(i + 1));
}
private Symbol(String ns_interned, String name_interned){
this.name = name_interned;
this.ns = ns_interned;
this._meta = null;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Symbol))
return false;
Symbol symbol = (Symbol) o;
return Util.equals(ns,symbol.ns) && name.equals(symbol.name);
}
public int hashCode(){
return Util.hashCombine(Util.hash(name), Util.hash(ns));
}
public int hasheq() {
if(_hasheq == 0){
_hasheq = Util.hashCombine(Murmur3.hashUnencodedChars(name), Util.hash(ns));
}
return _hasheq;
}
public IObj withMeta(IPersistentMap meta){
return new Symbol(meta, ns, name);
}
private Symbol(IPersistentMap meta, String ns, String name){
this.name = name;
this.ns = ns;
this._meta = meta;
}
public int compareTo(Object o){
Symbol s = (Symbol) o;
if(this.equals(o))
return 0;
if(this.ns == null && s.ns != null)
return -1;
if(this.ns != null)
{
if(s.ns == null)
return 1;
int nsc = this.ns.compareTo(s.ns);
if(nsc != 0)
return nsc;
}
return this.name.compareTo(s.name);
}
private Object readResolve() throws ObjectStreamException{
return intern(ns, name);
}
public Object invoke(Object obj) {
return RT.get(obj, this);
}
public Object invoke(Object obj, Object notFound) {
return RT.get(obj, this, notFound);
}
public IPersistentMap meta(){
return _meta;
}
}
================================================
FILE: src/jvm/clojure/lang/TaggedLiteral.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
public class TaggedLiteral implements ILookup {
public static final Keyword TAG_KW = Keyword.intern("tag");
public static final Keyword FORM_KW = Keyword.intern("form");
public final Symbol tag;
public final Object form;
public static TaggedLiteral create(Symbol tag, Object form) {
return new TaggedLiteral(tag, form);
}
private TaggedLiteral(Symbol tag, Object form){
this.tag = tag;
this.form = form;
}
public Object valAt(Object key) {
return valAt(key, null);
}
public Object valAt(Object key, Object notFound) {
if (FORM_KW.equals(key)) {
return this.form;
} else if (TAG_KW.equals(key)) {
return this.tag;
} else {
return notFound;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TaggedLiteral that = (TaggedLiteral) o;
if (form != null ? !form.equals(that.form) : that.form != null) return false;
if (tag != null ? !tag.equals(that.tag) : that.tag != null) return false;
return true;
}
@Override
public int hashCode() {
int result = Util.hash(tag);
result = 31 * result + Util.hash(form);
return result;
}
}
================================================
FILE: src/jvm/clojure/lang/ThreadFactory.java
================================================
package clojure.lang;
public interface ThreadFactory {
Thread newThread(java.lang.Runnable runnable);
}
================================================
FILE: src/jvm/clojure/lang/TransactionalHashMap.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 31, 2008 */
package clojure.lang;
import java.util.concurrent.ConcurrentMap;
import java.util.*;
public class TransactionalHashMap extends AbstractMap implements ConcurrentMap{
final Ref[] bins;
IPersistentMap mapAt(int bin){
return (IPersistentMap) bins[bin].deref();
}
final int binFor(Object k){
//spread hashes, a la Cliff Click
int h = Util.hash(k);
h ^= (h >>> 20) ^ (h >>> 12);
h ^= (h >>> 7) ^ (h >>> 4);
return h % bins.length;
// return Util.hash(k) % bins.length;
}
Entry entryAt(Object k){
return mapAt(binFor(k)).entryAt(k);
}
public TransactionalHashMap() {
this(421);
}
public TransactionalHashMap(int nBins) {
bins = new Ref[nBins];
for(int i = 0; i < nBins; i++)
bins[i] = new Ref(PersistentHashMap.EMPTY);
}
public TransactionalHashMap(Map extends K, ? extends V> m) {
this(m.size());
putAll(m);
}
public int size(){
int n = 0;
for(int i = 0; i < bins.length; i++)
{
n += mapAt(i).count();
}
return n;
}
public boolean isEmpty(){
return size() == 0;
}
public boolean containsKey(Object k){
return entryAt(k) != null;
}
public V get(Object k){
Entry e = entryAt(k);
if(e != null)
return (V) e.getValue();
return null;
}
public V put(K k, V v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Object ret = map.valAt(k);
r.set(map.assoc(k, v));
return (V) ret;
}
public V remove(Object k){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Object ret = map.valAt(k);
r.set(map.without(k));
return (V) ret;
}
public void putAll(Map extends K, ? extends V> map){
for(Iterator i = map.entrySet().iterator(); i.hasNext();)
{
Entry e = (Entry) i.next();
put(e.getKey(), e.getValue());
}
}
public void clear(){
for(int i = 0; i < bins.length; i++)
{
Ref r = bins[i];
IPersistentMap map = (IPersistentMap) r.deref();
if(map.count() > 0)
{
r.set(PersistentHashMap.EMPTY);
}
}
}
public Set> entrySet(){
final ArrayList> entries = new ArrayList(bins.length);
for(int i = 0; i < bins.length; i++)
{
IPersistentMap map = mapAt(i);
if(map.count() > 0)
entries.addAll((Collection) RT.seq(map));
}
return new AbstractSet>(){
public Iterator iterator(){
return Collections.unmodifiableList(entries).iterator();
}
public int size(){
return entries.size();
}
};
}
public V putIfAbsent(K k, V v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e == null)
{
r.set(map.assoc(k, v));
return null;
}
else
return (V) e.getValue();
}
public boolean remove(Object k, Object v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e != null && e.getValue().equals(v))
{
r.set(map.without(k));
return true;
}
return false;
}
public boolean replace(K k, V oldv, V newv){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e != null && e.getValue().equals(oldv))
{
r.set(map.assoc(k, newv));
return true;
}
return false;
}
public V replace(K k, V v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e != null)
{
r.set(map.assoc(k, v));
return (V) e.getValue();
}
return null;
}
}
================================================
FILE: src/jvm/clojure/lang/TransformerIterator.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* Alex Miller 3/3/15 */
package clojure.lang;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.LinkedList;
public class TransformerIterator implements Iterator {
private static final Buffer EMPTY = new Empty();
private static final Object NONE = new Object();
// Source
private final Iterator sourceIter;
private final IFn xf;
private final boolean multi;
// Iteration state
private volatile Buffer buffer = EMPTY;
private volatile Object next = NONE;
private volatile boolean completed = false;
private TransformerIterator(IFn xform, Iterator sourceIter, boolean multi) {
this.sourceIter = sourceIter;
this.xf = (IFn) xform.invoke(new AFn() {
public Object invoke() {
return null;
}
public Object invoke(Object acc) {
return acc;
}
public Object invoke(Object acc, Object o) {
buffer = buffer.add(o);
return acc;
}
});
this.multi = multi;
}
public static Iterator create(IFn xform, Iterator source) {
return new TransformerIterator(xform, source, false);
}
public static Iterator createMulti(IFn xform, List sources) {
Iterator[] iters = new Iterator[sources.size()];
for(int i=0; i> 2);
return seed;
}
static public boolean isPrimitive(Class c){
return c != null && c.isPrimitive() && !(c == Void.TYPE);
}
static public boolean isInteger(Object x){
return x instanceof Integer
|| x instanceof Long
|| x instanceof BigInt
|| x instanceof BigInteger;
}
static public Object ret1(Object ret, Object nil){
return ret;
}
static public ISeq ret1(ISeq ret, Object nil){
return ret;
}
static public void clearCache(ReferenceQueue rq, ConcurrentHashMap> cache){
//cleanup any dead entries
if(rq.poll() != null)
{
while(rq.poll() != null)
;
for(Map.Entry> e : cache.entrySet())
{
Reference val = e.getValue();
if(val != null && val.get() == null)
cache.remove(e.getKey(), val);
}
}
}
static public RuntimeException runtimeException(String s){
return new RuntimeException(s);
}
static public RuntimeException runtimeException(String s, Throwable e){
return new RuntimeException(s, e);
}
/**
* Throw even checked exceptions without being required
* to declare them or catch them. Suggested idiom:
*
* throw sneakyThrow( some exception );
*/
static public RuntimeException sneakyThrow(Throwable t) {
// http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
if (t == null)
throw new NullPointerException();
Util.sneakyThrow0(t);
return null;
}
@SuppressWarnings("unchecked")
static private void sneakyThrow0(Throwable t) throws T {
throw (T) t;
}
public static void trow(Throwable sneakyThrow) {
Util.sneakyThrow(sneakyThrow);
}
}
================================================
FILE: src/jvm/clojure/lang/Var.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 31, 2007 */
package clojure.lang;
import java.util.concurrent.atomic.AtomicBoolean;
public final class Var extends ARef implements IFn, IRef, Settable{
public static class TBox{
volatile Object val;
final Thread thread;
public TBox(Thread t, Object val){
this.thread = t;
this.val = val;
}
}
static public class Unbound extends AFn{
final public Var v;
public Unbound(Var v){
this.v = v;
}
public String toString(){
return "Unbound: " + v;
}
public Object throwArity(int n){
throw new IllegalStateException("Attempting to call unbound fn: " + v);
}
}
static class Frame{
final static Frame TOP = new Frame(PersistentHashMap.EMPTY, null);
//Var->TBox
Associative bindings;
//Var->val
// Associative frameBindings;
Frame prev;
public Frame(Associative bindings, Frame prev){
// this.frameBindings = frameBindings;
this.bindings = bindings;
this.prev = prev;
}
protected Object clone() {
return new Frame(this.bindings, null);
}
}
static final ThreadLocal dvals = new ThreadLocal(){
protected Frame initialValue(){
return Frame.TOP;
}
};
static public volatile int rev = 0;
static Keyword privateKey = Keyword.intern(null, "private");
static IPersistentMap privateMeta = new PersistentArrayMap(new Object[]{privateKey, Boolean.TRUE});
static Keyword macroKey = Keyword.intern(null, "macro");
static Keyword nameKey = Keyword.intern(null, "name");
static Keyword forceKey = Keyword.intern(null, "force");
static Keyword nsKey = Keyword.intern(null, "ns");
//static Keyword tagKey = Keyword.intern(null, "tag");
volatile Object root;
volatile boolean dynamic = false;
transient final AtomicBoolean threadBound;
public final Symbol sym;
public final Namespace ns;
//IPersistentMap _meta;
public static Object getThreadBindingFrame(){
return dvals.get();
}
public static Object cloneThreadBindingFrame(){
return dvals.get().clone();
}
public static void resetThreadBindingFrame(Object frame){
dvals.set((Frame) frame);
}
public Var setDynamic(){
this.dynamic = true;
return this;
}
public Var setDynamic(boolean b){
this.dynamic = b;
return this;
}
public final boolean isDynamic(){
return dynamic;
}
public static Var intern(Namespace ns, Symbol sym, Object root){
return intern(ns, sym, root, true);
}
public static Var intern(Namespace ns, Symbol sym, Object root, boolean replaceRoot){
Var dvout = ns.intern(sym);
if(!dvout.hasRoot() || replaceRoot)
dvout.bindRoot(root);
return dvout;
}
public String toString(){
if(ns != null)
return "#'" + ns.name + "/" + sym;
return "#";
}
public static Var find(Symbol nsQualifiedSym){
if(nsQualifiedSym.ns == null)
throw new IllegalArgumentException("Symbol must be namespace-qualified");
Namespace ns = Namespace.find(Symbol.intern(nsQualifiedSym.ns));
if(ns == null)
throw new IllegalArgumentException("No such namespace: " + nsQualifiedSym.ns);
return ns.findInternedVar(Symbol.intern(nsQualifiedSym.name));
}
public static Var intern(Symbol nsName, Symbol sym){
Namespace ns = Namespace.findOrCreate(nsName);
return intern(ns, sym);
}
public static Var internPrivate(String nsName, String sym){
Namespace ns = Namespace.findOrCreate(Symbol.intern(nsName));
Var ret = intern(ns, Symbol.intern(sym));
ret.setMeta(privateMeta);
return ret;
}
public static Var intern(Namespace ns, Symbol sym){
Var v = ns.intern(sym);
if (!v.isBound() && !ObjC.objc) {
maybeLoadFromClass(ns.toString(), sym.toString());
}
return v;
}
public static Var maybeLoadFromClass(String ns, String sym){
String ns_sym = buildClassName(ns, sym);
try {
Class c = Class.forName(ns_sym);
Var v = (Var) c.getDeclaredField("VAR").get(null);
return v;
} catch (Throwable e) {
return null;
}
}
private static String buildClassName(String ns, String sym) {
return clojure.lang.Compiler.munge(ns + Compiler.DOLLAR + sym.replace(".", "_DOT_"));
}
public void maybeLoad() {
if (ObjC.objc) {
throw new RuntimeException("Dynamic loading not available on objc");
//loadFromClass(buildClassName(ns.toString(), sym.toString()));
} else {
maybeLoadFromClass(ns.toString(), sym.toString());
}
}
//private native void loadFromClass(String clazz) /*-[
// IOSObjectArray *parts = [clazz split:@"\\."];
// NSString *classname = @"";
// for (int n = 0; n < parts.count - 1; n++) {
// NSString *s = (NSString*)[parts objectAtIndex:n];
// classname = [classname stringByAppendingString:[[[s substringToIndex:1] uppercaseString] stringByAppendingString:[s substringFromIndex:1]]];
// }
// classname = [classname stringByAppendingString:[parts objectAtIndex:parts.count-1]];
// [NSClassFromString(classname) VAR];
//]-*/;
public static Var create(){
return new Var(null, null);
}
public static Var create(Object root){
return new Var(null, null, root);
}
Var(Namespace ns, Symbol sym){
this.ns = ns;
this.sym = sym;
this.threadBound = new AtomicBoolean(false);
this.root = new Unbound(this);
setMeta(PersistentHashMap.EMPTY);
}
Var(Namespace ns, Symbol sym, Object root){
this(ns, sym);
this.root = root;
++rev;
}
public boolean isBound(){
return hasRoot() || (threadBound.get() && dvals.get().bindings.containsKey(this));
}
final public Object get(){
if(!threadBound.get())
return root;
return deref();
}
final public Object deref(){
TBox b = getThreadBinding();
if(b != null)
return b.val;
return root;
}
public void setValidator(IFn vf){
if(hasRoot())
validate(vf, root);
validator = vf;
}
public Object alter(IFn fn, ISeq args) {
set(fn.applyTo(RT.cons(deref(), args)));
return this;
}
public Object set(Object val){
validate(getValidator(), val);
TBox b = getThreadBinding();
if(b != null)
{
if(Thread.currentThread() != b.thread)
throw new IllegalStateException(String.format("Can't set!: %s from non-binding thread", sym));
return (b.val = val);
}
// TODO!
if (!ObjC.objc) {
throw new IllegalStateException(String.format("Can't change/establish root binding of: %s with set", sym));
} else {
return null;
}
}
public Object doSet(Object val) {
return set(val);
}
public Object doReset(Object val) {
bindRoot(val);
return val;
}
public void setMeta(IPersistentMap m) {
resetMeta(m);
}
@Override
public IPersistentMap meta() {
// ensure these basis keys
return super.meta().assoc(nameKey, sym).assoc(nsKey, ns);
}
public void setMacro() {
alterMeta(assoc, RT.list(macroKey, RT.T));
}
public boolean isMacro(){
return RT.booleanCast(meta().valAt(macroKey));
}
//public void setExported(boolean state){
// _meta = _meta.assoc(privateKey, state);
//}
public boolean isPublic(){
return !RT.booleanCast(meta().valAt(privateKey));
}
final public Object getRawRoot(){
return root;
}
public Object getTag(){
return meta().valAt(RT.TAG_KEY);
}
public void setTag(Symbol tag) {
alterMeta(assoc, RT.list(RT.TAG_KEY, tag));
}
final public boolean hasRoot(){
return !(root instanceof Unbound);
}
//binding root always clears macro flag
synchronized public void bindRoot(Object root){
validate(getValidator(), root);
Object oldroot = this.root;
this.root = root;
++rev;
notifyWatches(oldroot,this.root);
}
synchronized void swapRoot(Object root){
validate(getValidator(), root);
Object oldroot = this.root;
this.root = root;
++rev;
notifyWatches(oldroot,root);
}
synchronized public void unbindRoot(){
this.root = new Unbound(this);
++rev;
}
synchronized public void commuteRoot(IFn fn) {
Object newRoot = fn.invoke(root);
validate(getValidator(), newRoot);
Object oldroot = root;
this.root = newRoot;
++rev;
notifyWatches(oldroot,newRoot);
}
synchronized public Object alterRoot(IFn fn, ISeq args) {
Object newRoot = fn.applyTo(RT.cons(root, args));
validate(getValidator(), newRoot);
Object oldroot = root;
this.root = newRoot;
++rev;
notifyWatches(oldroot,newRoot);
return newRoot;
}
public static void pushThreadBindings(Associative bindings){
Frame f = dvals.get();
Associative bmap = f.bindings;
for(ISeq bs = bindings.seq(); bs != null; bs = bs.next())
{
IMapEntry e = (IMapEntry) bs.first();
Var v = (Var) e.key();
if(!v.dynamic)
throw new IllegalStateException(String.format("Can't dynamically bind non-dynamic var: %s/%s", v.ns, v.sym));
v.validate(v.getValidator(), e.val());
v.threadBound.set(true);
bmap = bmap.assoc(v, new TBox(Thread.currentThread(), e.val()));
}
dvals.set(new Frame(bmap, f));
}
public static void popThreadBindings(){
Frame f = dvals.get().prev;
if (f == null) {
throw new IllegalStateException("Pop without matching push");
} else if (f == Frame.TOP) {
dvals.remove();
} else {
dvals.set(f);
}
}
public static Associative getThreadBindings(){
Frame f = dvals.get();
IPersistentMap ret = PersistentHashMap.EMPTY;
for(ISeq bs = f.bindings.seq(); bs != null; bs = bs.next())
{
IMapEntry e = (IMapEntry) bs.first();
Var v = (Var) e.key();
TBox b = (TBox) e.val();
ret = ret.assoc(v, b.val);
}
return ret;
}
public final TBox getThreadBinding(){
if(threadBound.get())
{
IMapEntry e = dvals.get().bindings.entryAt(this);
if(e != null)
return (TBox) e.val();
}
return null;
}
final public IFn fn(){
return (IFn) deref();
}
public Object call() {
return invoke();
}
public void run(){
invoke();
}
public Object invoke() {
return fn().invoke();
}
public Object invoke(Object arg1) {
return fn().invoke(Util.ret1(arg1,arg1=null));
}
public Object invoke(Object arg1, Object arg2) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
{
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
{
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
{
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null),
Util.ret1(arg16,arg16=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null),
Util.ret1(arg16,arg16=null),
Util.ret1(arg17,arg17=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null),
Util.ret1(arg16,arg16=null),
Util.ret1(arg17,arg17=null),
Util.ret1(arg18,arg18=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null),
Util.ret1(arg16,arg16=null),
Util.ret1(arg17,arg17=null),
Util.ret1(arg18,arg18=null),
Util.ret1(arg19,arg19=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
{
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null),
Util.ret1(arg16,arg16=null),
Util.ret1(arg17,arg17=null),
Util.ret1(arg18,arg18=null),
Util.ret1(arg19,arg19=null),
Util.ret1(arg20,arg20=null));
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
Object... args)
{
return fn().invoke(Util.ret1(arg1,arg1=null),
Util.ret1(arg2,arg2=null),
Util.ret1(arg3,arg3=null),
Util.ret1(arg4,arg4=null),
Util.ret1(arg5,arg5=null),
Util.ret1(arg6,arg6=null),
Util.ret1(arg7,arg7=null),
Util.ret1(arg8,arg8=null),
Util.ret1(arg9,arg9=null),
Util.ret1(arg10,arg10=null),
Util.ret1(arg11,arg11=null),
Util.ret1(arg12,arg12=null),
Util.ret1(arg13,arg13=null),
Util.ret1(arg14,arg14=null),
Util.ret1(arg15,arg15=null),
Util.ret1(arg16,arg16=null),
Util.ret1(arg17,arg17=null),
Util.ret1(arg18,arg18=null),
Util.ret1(arg19,arg19=null),
Util.ret1(arg20,arg20=null),
(Object[])Util.ret1(args, args=null));
}
public Object applyTo(ISeq arglist) {
return AFn.applyToHelper(this, arglist);
}
static IFn assoc = new AFn(){
@Override
public Object invoke(Object m, Object k, Object v) {
return RT.assoc(m, k, v);
}
};
static IFn dissoc = new AFn() {
@Override
public Object invoke(Object c, Object k) {
return RT.dissoc(c, k);
}
};
}
================================================
FILE: src/jvm/clojure/lang/Volatile.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
final public class Volatile implements IDeref {
volatile Object val;
public Volatile(Object val){
this.val = val;
}
public Object deref() {
return val;
}
public Object reset(Object newval) {
return this.val = newval;
}
}
================================================
FILE: src/jvm/clojure/lang/WarnBoxedMath.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WarnBoxedMath {
boolean value() default true;
}
================================================
FILE: src/jvm/clojure/lang/XMLHandler.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 17, 2007 */
package clojure.lang;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class XMLHandler extends DefaultHandler{
ContentHandler h;
public XMLHandler(ContentHandler h){
this.h = h;
}
public void setDocumentLocator(Locator locator){
h.setDocumentLocator(locator);
}
public void startDocument() throws SAXException{
h.startDocument();
}
public void endDocument() throws SAXException{
h.endDocument();
}
public void startPrefixMapping(String prefix, String uri) throws SAXException{
h.startPrefixMapping(prefix, uri);
}
public void endPrefixMapping(String prefix) throws SAXException{
h.endPrefixMapping(prefix);
}
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
h.startElement(uri, localName, qName, atts);
}
public void endElement(String uri, String localName, String qName) throws SAXException{
h.endElement(uri, localName, qName);
}
public void characters(char ch[], int start, int length) throws SAXException{
h.characters(ch, start, length);
}
public void ignorableWhitespace(char ch[], int start, int length) throws SAXException{
h.ignorableWhitespace(ch, start, length);
}
public void processingInstruction(String target, String data) throws SAXException{
h.processingInstruction(target, data);
}
public void skippedEntity(String name) throws SAXException{
h.skippedEntity(name);
}
/*
public static void main(String[] args){
try
{
ContentHandler dummy = new DefaultHandler();
SAXParserFactory f = SAXParserFactory.newInstance();
//f.setNamespaceAware(true);
SAXParser p = f.newSAXParser();
p.parse("http://arstechnica.com/journals.rssx",new XMLHandler(dummy));
}
catch(Exception e)
{
e.printStackTrace();
}
}
//*/
}
================================================
FILE: src/jvm/clojure/lang/package.html
================================================
Clojure language implementation.
The clojure.lang package holds the implementation for Clojure.
The only class considered part of the public API is
{@link clojure.lang.IFn}. All other classes should be considered
implementation details.
================================================
FILE: src/jvm/clojure/main.java
================================================
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure;
import clojure.lang.RT;
import clojure.lang.Symbol;
import clojure.lang.Var;
public class main{
final static private Symbol CLOJURE_MAIN = Symbol.intern("clojure.main");
final static private Var REQUIRE = RT.var("clojure.core", "require");
final static private Var MAIN = RT.var("clojure.main", "main");
public static void main(String[] args) {
REQUIRE.invoke(CLOJURE_MAIN);
MAIN.applyTo(RT.seq(args));
}
}
================================================
FILE: src/jvm/com/google/j2objc/annotations/ReflectionSupport.java
================================================
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.j2objc.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that specifies the level of reflection support for a particular
* class.
*
* @author Keith Stanger
*/
@Target({ ElementType.TYPE, ElementType.PACKAGE })
@Retention(RetentionPolicy.SOURCE)
public @interface ReflectionSupport {
/**
* Enumerates the available levels of reflection support.
*/
enum Level {
/*
* No metadata is emitted, so reflection support is limited to the
* information that can be obtained from the Objective-C runtime.
*/
NATIVE_ONLY,
/*
* Additional metadata is emitted, allowing for full reflection support.
*/
FULL
}
Level value();
}
================================================
FILE: src/objc/Cst502Socket.h
================================================
/**
* Cst502Socket.m - Simple objective-c class for manipulating stream sockets.
* Purpose: demonstrate stream sockets in Objective-C.
* These examples are buildable on MacOSX and GNUstep on top of Windows7
* Cst502 Emerging Languages and Programming Technologies
* See http://pooh.poly.asu.edu/Cst502
* @author Tim Lindquist (Tim.Lindquist@asu.edu), ASU Polytechnic, Engineering
* @version December 2011
*/
#import
#include
#include
#include
#include
#include
#include
#if defined(WINGS)
#define _WIN32_WINNT 0x0501
#include
#include
#else
#include
#include
#include
#include
#include
#include
#include
#endif /* !WINGS */
#define BACKLOG 10 /* queue size for pending connect requests */
#define MAXDATASIZE 4096 /* 4K bytes maximum with a single receive */
/**
* Cst502Socket.h - objective-c class for manipulating stream sockets.
* Purpose: demonstrate stream sockets in Objective-C.
* These examples are buildable on MacOSX and GNUstep on top of Windows7
* Cst502 Emerging Languages and Programming Technologies
* See http://pooh.poly.asu.edu/Cst502
* @author Tim Lindquist (Tim.Lindquist@asu.edu), ASU Polytechnic, Engineering
* @version December 2011
* based on the simple server and client sockets in C by Jeez.
*/
@interface Cst502ServerSocket : NSObject {
int sockfd, new_fd, rv;
BOOL connected;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;
socklen_t sin_size;
struct in_addr address;
}
- (id) initWithPort: (NSString*) port;
- (BOOL) accept;
- (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at;
- (NSString*) receiveBytes: (char*) byteMsg
maxBytes: (int) max
beginAt: (int) at;
- (BOOL) close;
@end
@interface Cst502ClientSocket : NSObject {
BOOL connected;
int sockfd, numbytes, rv;
struct addrinfo hints, *servinfo, *p;
NSString *hostName, *portNum;
char s[INET6_ADDRSTRLEN];
}
- (id) initWithHost: (NSString*) host portNumber: (NSString*) port;
- (BOOL) connect;
- (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at;
- (NSString*) receiveBytes: (char*) byteMsg
maxBytes: (int) max
beginAt: (int) at;
- (void) sendString: (NSString*)s;
- (BOOL) close;
@end
================================================
FILE: src/objc/Cst502Socket.m
================================================
#import "Cst502Socket.h"
#define PORT "4444"
/**
* Cst502Socket.m - objective-c class for manipulating stream sockets.
* Purpose: demonstrate stream sockets in Objective-C.
* These examples are buildable on MacOSX and GNUstep on top of Windows7
* Cst502 Emerging Languages and Programming Technologies
* See http://pooh.poly.asu.edu/Cst502
* @author Tim Lindquist (Tim.Lindquist@asu.edu), ASU Polytechnic, Engineering
* based on the simple server and client sockets in C by Jeez.
* @version December 2011
*/
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
@implementation Cst502ServerSocket
- (id) initWithPort: (NSString*) port{
self = [super init];
int ret = 0;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
const char* portStr = [port UTF8String];
if ((rv = getaddrinfo(NULL, portStr, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
ret = 1;
}else{
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1){
perror("server: socket create error");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
#if defined(WINGS)
closesocket(sockfd);
#else
close(sockfd);
#endif
perror("server: bind error");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
ret = 2;
}else{
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("server: listen error");
ret = 3;
}
}
if (ret == 0){
return self;
}
}
return nil;
}
- (BOOL) accept {
BOOL ret = YES;
#if defined(WINGS)
new_fd = accept(sockfd, NULL, NULL);
#else
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
#endif
if (new_fd == -1) {
perror("server: accept error");
ret = NO;
}
connected = ret;
return ret;
}
- (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at{
int ret = send(new_fd, byteMsg, msgLength, 0);
if(ret == -1){
NSLog(@"error sending bytes");
}
return ret;
}
- (NSString* ) receiveBytes: (char*) byteMsg
maxBytes: (int) max
beginAt: (int) at {
int ret = recv(new_fd, byteMsg, max-1, at);
if(ret == -1){
NSLog(@"server error receiving bytes");
}
byteMsg[ret+at] = '\0';
NSString * retStr = [NSString stringWithUTF8String: byteMsg];
return retStr;
}
- (BOOL) close{
#if defined(WINGS)
closesocket(new_fd);
#else
close(new_fd);
#endif
connected = NO;
return YES;
}
- (void) dealloc {
#if defined(WINGS)
closesocket(sockfd);
#else
close(sockfd);
#endif
[super dealloc];
}
@end
@implementation Cst502ClientSocket
- (id) initWithHost: (NSString*) host portNumber: (NSString*) port {
self = [super init];
hostName = host;
[hostName retain];
portNum = port;
[portNum retain];
return self;
}
- (BOOL) connect {
connected = YES;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo([hostName UTF8String], [portNum UTF8String],
&hints, &servinfo)) != 0) {
fprintf(stderr, "client error getting host address: %s\n",
gai_strerror(rv));
connected = NO;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1){
perror("client error creating socket");
connected = NO;
continue;
}
int callret = connect(sockfd, p->ai_addr, p->ai_addrlen);
if (callret == -1) {
#if defined(WINGS)
closesocket(sockfd);
#else
close(sockfd);
#endif
#if defined(WINGS)
//printf("client failed to connect.\n");
#else
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
// printf("client failed to connect to %s\n", s);
#endif
//perror("client error connecting");
connected = NO;
continue;
}
break;
}
if (p == NULL) {
// printf("client failed to connect\n");
connected = NO;
}else{
#if defined(WINGS)
//printf("client connected\n");
#else
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
//printf("client connected to %s\n", s);
#endif
connected = YES;
}
return connected;
}
- (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at{
int ret = send(sockfd, byteMsg, msgLength, 0);
if(ret == -1){
NSLog(@"client error sending bytes");
}
return ret;
}
- (NSString*) receiveBytes: (char*) byteMsg
maxBytes: (int) max
beginAt: (int) at {
int ret = recv(sockfd, byteMsg, max-1, at);
if(ret == -1){
NSLog(@"client error receiving bytes");
}
byteMsg[ret+at] = '\0';
NSString * retStr = [NSString stringWithUTF8String: byteMsg];
return retStr;
}
- (void) sendString: (NSString*)str {
[self sendBytes:(char*)[str UTF8String] OfLength:[str length] Index:0];
}
- (BOOL) close{
connected = NO;
return YES;
}
- (void) dealloc {
[hostName release];
[portNum release];
[super dealloc];
}
@end
================================================
FILE: src/objc/NSCommon.h
================================================
//
// NSCommon.h
// sample
//
// Created by Gal Dolber on 2/4/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
// Supported types
// -------------------
// float
// long long
// long
// char
// short
// int
// double
// long double
// unsigned long long
// unsigned long
// unsigned char
// unsigned short
// unsigned int
// bool
// CGPoint
// NSRange
// UIEdgeInsets
// CGSize
// CGAffineTransform
// CATransform3D
// UIOffset
// CGRect
// id
// void
#import
#import "clojure/lang/AFn.h"
#import "clojure/lang/Atom.h"
static ClojureLangAtom *dynamicClasses;
#define to_char(c)\
[(JavaLangCharacter*)c charValue]\
// Necessary for inline functions
static NSMutableDictionary *global_functions;
#define register_fn(n)\
[global_functions setObject:[NSValue valueWithPointer:n] forKey:@#n];\
static const char void_type = 'v';
static const char float_type = 'f';
static const char longlong_type = 'q';
static const char long_type = 'l';
static const char char_type = 'c';
static const char short_type = 's';
static const char int_type = 'i';
static const char double_type = 'd';
static const char longdouble_type = 'D';
static const char ulonglong_type = 'Q';
static const char ulong_type = 'L';
static const char uchar_type = 'C';
static const char ushort_type = 'S';
static const char uint_type = 'I';
static const char bool_type = 'b';
static const char cgpoint_type = 'P';
static const char nsrange_type = 'N';
static const char uiedge_type = 'E';
static const char cgsize_type = 'Z';
static const char cgaffinetransform_type = 'A';
static const char catransform3d_type = 'T';
static const char uioffset_type = 'O';
static const char cgrect_type = 'R';
static const char id_type = 'p';
static const char pointer_type = 'Y';
char signatureToType(const char* c);
id signaturesToTypes(NSMethodSignature* sig, BOOL skip);
const char* makeSignature(id types);
void* callWithArgs(void **argsp, id sself, id types, ClojureLangAFn *fn);
void callWithInvocation(NSInvocation *invocation, id sself, id types, ClojureLangAFn *fn);
@interface NSCommon : NSObject
+(BOOL)cgfloatIsDouble;
+(id)ccall:(id)name types:(id)types args:(id)args;
+(id)invokeFun:(NSString*)fun withSelf:(id)object withSelector:(NSString*)selector withArgs:(id)arguments;
+(id)invokeSel:(id)object withSelector:(NSString*)selector withArgs:(id)arguments;
+(id)invokeSuperSel:(id)object withDispatchClass:(id)clazz withSelector:(NSString*)selector withArgs:(id)arguments;
@end
================================================
FILE: src/objc/NSCommon.m
================================================
//
// NSCommon.m
// sample
//
// Created by Gal Dolber on 2/4/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import "NSCommon.h"
#import "clojure/lang/RemoteRepl.h"
#import "clojure/lang/AFn.h"
#import "clojure/lang/RT.h"
#import "clojure/lang/Atom.h"
#import "clojure/lang/PersistentVector.h"
#import "clojure/lang/PersistentHashMap.h"
#import "clojure/lang/Selector.h"
#import "clojure/lang/Var.h"
#import "java/lang/Character.h"
#import "java/lang/Boolean.h"
#import "java/lang/Integer.h"
#import "java/lang/Double.h"
#import "java/lang/Float.h"
#import "java/lang/Long.h"
#import "java/lang/Short.h"
#import "ffi.h"
#import "objc/runtime.h"
#import "objc/message.h"
#import
#import "WeakRef.h"
#import
static id cons;
static id fconj;
static id assoc;
static bool classIsDynamic(Class clazz) {
return [ClojureLangRT getWithId:[dynamicClasses deref] withId:NSStringFromClass(clazz)] != nil;
}
#if CGFLOAT_IS_DOUBLE
#define CGFloatFFI &ffi_type_double
#else
#define CGFloatFFI &ffi_type_float
#endif
static ffi_type CGPointFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [3]){CGFloatFFI, CGFloatFFI, NULL}};
static ffi_type CGSizeFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [3]){CGFloatFFI, CGFloatFFI, NULL}};
static ffi_type CGRectFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [3]){&CGPointFFI, &CGSizeFFI, NULL}};
static ffi_type NSRangeFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [3]){&ffi_type_uint, &ffi_type_uint, NULL}};
static ffi_type UIEdgeInsetsFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [5]){CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI, NULL}};
static ffi_type UIOffsetFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [3]){CGFloatFFI, CGFloatFFI, NULL}};
static ffi_type CATransform3DFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [17]){
CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI,
CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI,
CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI,
CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI,
NULL}};
static ffi_type CGAffineTransformFFI = (ffi_type){
.size = 0,
.alignment = 0,
.type = FFI_TYPE_STRUCT,
.elements = (ffi_type * [7]){
CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI, CGFloatFFI, NULL}};
const char* encode_type(char d) {
switch (d) {
case void_type: return @encode(void);
case float_type: return @encode(float);
case long_type: return @encode(long);
case longlong_type: return @encode(long long);
case char_type: return @encode(char);
case short_type: return @encode(short);
case int_type: return @encode(int);
case double_type: return @encode(double);
case longdouble_type: return @encode(long double);
case ulong_type: return @encode(unsigned long);
case ulonglong_type: return @encode(unsigned long long);
case uchar_type: return @encode(unsigned char);
case ushort_type: return @encode(unsigned short);
case uint_type: return @encode(unsigned int);
case bool_type: return @encode(BOOL);
case id_type: return @encode(id);
case cgpoint_type: return @encode(CGPoint);
case nsrange_type: return @encode(NSRange);
case uiedge_type: return @encode(UIEdgeInsets);
case cgsize_type: return @encode(CGSize);
case cgaffinetransform_type: return @encode(CGAffineTransform);
case catransform3d_type: return @encode(CATransform3D);
case uioffset_type: return @encode(UIOffset);
case cgrect_type: return @encode(CGRect);
case pointer_type: return @encode(void*);
}
return @encode(id);
}
void * ffi_type_for_type(char type) {
switch (type) {
case void_type: return &ffi_type_void;
case float_type: return &ffi_type_float;
case longlong_type: return &ffi_type_sint64;
case long_type: return &ffi_type_slong;
case char_type: return &ffi_type_schar;
case short_type: return &ffi_type_sshort;
case int_type: return &ffi_type_sint;
case double_type: return &ffi_type_double;
case longdouble_type: return &ffi_type_longdouble;
case ulonglong_type: return &ffi_type_uint64;
case ulong_type: return &ffi_type_ulong;
case uchar_type: return &ffi_type_uchar;
case ushort_type: return &ffi_type_ushort;
case uint_type: return &ffi_type_uint;
case bool_type: return &ffi_type_schar;
case cgpoint_type: return &CGPointFFI;
case nsrange_type: return &NSRangeFFI;
case uiedge_type: return &UIEdgeInsetsFFI;
case cgsize_type: return &CGSizeFFI;
case cgaffinetransform_type: return &CGAffineTransformFFI;
case catransform3d_type: return &CATransform3DFFI;
case uioffset_type: return &UIOffsetFFI;
case cgrect_type: return &CGRectFFI;
default: return &ffi_type_pointer;
}
}
int sizeof_type(char c) {
switch (c) {
case void_type: return sizeof(void);
case float_type: return sizeof(float);
case long_type: return sizeof(long);
case longlong_type: return sizeof(long long);
case char_type: return sizeof(char);
case short_type: return sizeof(short);
case int_type: return sizeof(int);
case double_type: return sizeof(double);
case longdouble_type: return sizeof(long double);
case ulong_type: return sizeof(unsigned long);
case ulonglong_type: return sizeof(unsigned long long);
case uchar_type: return sizeof(unsigned char);
case ushort_type: return sizeof(unsigned short);
case uint_type: return sizeof(unsigned int);
case bool_type: return sizeof(BOOL);
case cgpoint_type: return sizeof(CGPoint);
case nsrange_type: return sizeof(NSRange);
case uiedge_type: return sizeof(UIEdgeInsets);
case cgsize_type: return sizeof(CGSize);
case cgaffinetransform_type: return sizeof(CGAffineTransform);
case catransform3d_type: return sizeof(CATransform3D);
case uioffset_type: return sizeof(UIOffset);
case cgrect_type: return sizeof(CGRect);
case pointer_type: return sizeof(void*);
}
return sizeof(id);
}
id signaturesToTypes(NSMethodSignature* sig, BOOL skip) {
id types = ClojureLangPersistentVector_get_EMPTY_();
types = [fconj invokeWithId:types withId:[[[JavaLangCharacter alloc] initWithChar:signatureToType([sig methodReturnType])] autorelease]];
for (int n = 0; n < [sig numberOfArguments]; n++) {
if (!skip || (n != 0 && n != 1)) {
types = [fconj invokeWithId:types withId:[[[JavaLangCharacter alloc] initWithChar: signatureToType([sig getArgumentTypeAtIndex:n])] autorelease]];
}
}
return types;
}
// https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
char signatureToType(const char* c) {
switch (*c) {
case _C_CONST: // const
case 'n': // in
case 'N': // inout
case 'o': // out
case 'O': // bycopy
case 'R': // byref
case 'V': // oneway
c++;
return signatureToType(c);
case _C_FLT: return float_type;
case _C_LNG_LNG: return longlong_type;
case _C_LNG: return long_type;
case _C_CHR: return char_type;
case _C_SHT: return short_type;
case _C_INT: return int_type;
case _C_BOOL: return bool_type;
case _C_DBL: return double_type;
case _C_ULNG_LNG: return ulonglong_type;
case _C_ULNG: return ulong_type;
case _C_UCHR: return uchar_type;
case _C_USHT: return ushort_type;
case _C_UINT: return uint_type;
case _C_VOID: return void_type;
case 'D': return longdouble_type;
case _C_CHARPTR:
case _C_SEL:
case _C_PTR:
return pointer_type;
case _C_CLASS:
case _C_ID:
case _C_UNDEF:
return id_type;
case _C_STRUCT_B: {
if (strcmp(c, @encode(CGPoint)) == 0) {
return cgpoint_type;
} else if (strcmp(c, @encode(NSRange)) == 0) {
return nsrange_type;
} else if (strcmp(c, @encode(UIEdgeInsets)) == 0) {
return uiedge_type;
} else if (strcmp(c, @encode(CGSize)) == 0) {
return cgsize_type;
} else if (strcmp(c, @encode(CGAffineTransform)) == 0) {
return cgaffinetransform_type;
} else if (strcmp(c, @encode(CATransform3D)) == 0) {
return catransform3d_type;
} else if (strcmp(c, @encode(UIOffset)) == 0) {
return uioffset_type;
} else if (strcmp(c, @encode(CGRect)) == 0) {
return cgrect_type;
}
}
}
@throw [NSException exceptionWithName:@"Type signature not found" reason:[NSString stringWithUTF8String:c] userInfo:nil];
}
void * malloc_ret(char c) {
if (c == void_type) {
return malloc(sizeof_type(id_type));
}
return malloc(sizeof_type(c));
}
// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf
BOOL use_stret(char ret) {
switch (ret) {
case cgrect_type:
case cgpoint_type:
case nsrange_type:
case uiedge_type:
case cgsize_type:
case cgaffinetransform_type:
case catransform3d_type:
case uioffset_type: {
#if TARGET_IPHONE_SIMULATOR
return sizeof_type(ret) > 8;
#else
return sizeof_type(ret) >= 8;
#endif
break;
}
}
return NO;
}
#define pval(type)\
(type)*((type*)argsp[j])\
void* callWithArgs(void **argsp, id sself, id types, ClojureLangAFn *fn) {
id args = ClojureLangPersistentVector_get_EMPTY_();
args = [fconj invokeWithId:args withId:[WeakRef from:sself]];
ClojureLangPersistentVector *typesa = [ClojureLangPersistentVector createWithClojureLangISeq:types];
char retType = to_char([typesa nthWithInt:0]);
long typesc = [typesa count];
for (int n = 2; n < typesc; n++) {
id val = nil;
int j = n - 2;
switch (to_char([typesa nthWithInt:n])) {
case void_type: {
break;
}
case float_type: {
val = [ClojureLangRT boxWithFloat:pval(float)];
break;
}
case longlong_type: {
val = [ClojureLangRT boxWithLong:pval(long long)];
break;
}
case long_type: {
val = [ClojureLangRT boxWithLong:pval(long)];
break;
}
case char_type: {
char c = pval(char);
val = c == YES ? JavaLangBoolean_get_TRUE__() : (c == NO ? JavaLangBoolean_get_FALSE__() : [ClojureLangRT boxWithChar:pval(char)]);
break;
}
case short_type: {
val = [ClojureLangRT boxWithShort:pval(short)];
break;
}
case int_type: {
val = [ClojureLangRT boxWithInt:pval(int)];
break;
}
case longdouble_type: {
val = [ClojureLangRT boxWithDouble:pval(long double)];
break;
}
case double_type: {
val = [ClojureLangRT boxWithDouble:pval(double)];
break;
}
case ulong_type: {
val = [ClojureLangRT boxWithLong:pval(unsigned long)];
break;
}
case ulonglong_type: {
val = [ClojureLangRT boxWithLong:pval(unsigned long long)];
break;
}
case uchar_type: {
val = [ClojureLangRT boxWithChar:pval(unsigned char)];
break;
}
case ushort_type: {
val = [ClojureLangRT boxWithShort:pval(unsigned short)];
break;
}
case uint_type: {
val = [ClojureLangRT boxWithInt:pval(unsigned int)];
break;
}
case bool_type: {
val = pval(char) == YES ? JavaLangBoolean_get_TRUE__() : JavaLangBoolean_get_FALSE__();
break;
}
case id_type: {
val = pval(void*);
break;
}
case cgpoint_type: {
val = [NSValue valueWithCGPoint:pval(CGPoint)];
break;
}
case nsrange_type: {
val = [NSValue valueWithRange:pval(NSRange)];
break;
}
case uiedge_type: {
val = [NSValue valueWithUIEdgeInsets:pval(UIEdgeInsets)];
break;
}
case cgsize_type: {
val = [NSValue valueWithCGSize:pval(CGSize)];
break;
}
case cgaffinetransform_type: {
val = [NSValue valueWithCGAffineTransform:pval(CGAffineTransform)];
break;
}
case catransform3d_type: {
val = [NSValue valueWithCATransform3D:pval(CATransform3D)];
break;
}
case uioffset_type: {
val = [NSValue valueWithUIOffset:pval(UIOffset)];
break;
}
case cgrect_type: {
val = [NSValue valueWithCGRect:pval(CGRect)];
break;
}
case pointer_type: {
val = [NSValue valueWithPointer:pval(void*)];
break;
}
default: @throw [NSException exceptionWithName:@"Error"
reason:[NSString stringWithFormat:@"%@",
[typesa nthWithInt:n]] userInfo:nil];
}
args = [fconj invokeWithId:args withId:val];
free(argsp[j]);
}
id v = [fn applyToWithClojureLangISeq:[ClojureLangRT seqWithId:args]];
void * ret;
switch (retType) {
case void_type: {
id r = nil;
ret = &r;
break;
}
case float_type: {
float o = [ClojureLangRT floatCastWithId:v];
ret = &o;
break;
}
case longlong_type: {
long long o = [ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case long_type: {
long o = (long)[ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case char_type: {
char o = [ClojureLangRT charCastWithId:v];
ret = &o;
break;
}
case short_type: {
short o = [ClojureLangRT shortCastWithId:v];
ret = &o;
break;
}
case int_type: {
int o = [ClojureLangRT intCastWithId:v];
ret = &o;
break;
}
case longdouble_type: {
long double o = [ClojureLangRT doubleCastWithId:v];
ret = &o;
break;
}
case double_type: {
double o = [ClojureLangRT doubleCastWithId:v];
ret = &o;
break;
}
case ulong_type: {
unsigned long o = (unsigned long)[ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case ulonglong_type: {
unsigned long long o = [ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case uchar_type: {
unsigned char o = [ClojureLangRT charCastWithId:v];
ret = &o;
break;
}
case ushort_type: {
unsigned short o = [ClojureLangRT shortCastWithId:v];
ret = &o;
break;
}
case uint_type: {
unsigned int o = [ClojureLangRT intCastWithId:v];
ret = &o;
break;
}
case bool_type: {
BOOL o = [ClojureLangRT booleanCastWithId:v];
ret = &o;
break;
}
case id_type: {
ret = &v;
break;
}
case cgpoint_type: {
CGPoint o = [((NSValue*) v) CGPointValue];
ret = &o;
break;
}
case nsrange_type: {
NSRange o = [((NSValue*) v) rangeValue];
ret = &o;
break;
}
case uiedge_type: {
UIEdgeInsets o = [((NSValue*) v) UIEdgeInsetsValue];
ret = &o;
break;
}
case cgsize_type: {
CGSize o = [((NSValue*) v) CGSizeValue];
ret = &o;
break;
}
case cgaffinetransform_type: {
CGAffineTransform o = [((NSValue*) v) CGAffineTransformValue];
ret = &o;
break;
}
case catransform3d_type: {
CATransform3D o = [((NSValue*) v) CATransform3DValue];
ret = &o;
break;
}
case uioffset_type: {
UIOffset o = [((NSValue*) v) UIOffsetValue];
ret = &o;
break;
}
case cgrect_type: {
CGRect o = [((NSValue*) v) CGRectValue];
ret = &o;
break;
}
case pointer_type: {
void* o = [((NSValue*) v) pointerValue];
ret = &o;
break;
}
default: {
@throw [NSException exceptionWithName:@"Missing type" reason:[NSString stringWithFormat:@"%c", retType] userInfo:nil];
}
}
return ret;
}
void callWithInvocation(NSInvocation *invocation, id sself, id types, ClojureLangAFn *fn) {
id args = ClojureLangPersistentVector_get_EMPTY_();
args = [fconj invokeWithId:args withId:[WeakRef from:sself]];
IOSObjectArray *typesa = [ClojureLangRT toArrayWithId:types];
char retType = to_char([typesa objectAtIndex:0]);
long typesc = [typesa length];
for (int n = 1; n < typesc; n++) {
id val = nil;
int j = n + 1;
switch (to_char([typesa objectAtIndex:n])) {
case void_type: {
break;
}
case float_type: {
float v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithFloat:v];
break;
}
case longlong_type: {
long long v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithLong:v];
break;
}
case long_type: {
long v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithLong:v];
break;
}
case char_type: {
char v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithChar:v];
break;
}
case short_type: {
short v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithShort:v];
break;
}
case int_type: {
int v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithInt:v];
break;
}
case longdouble_type: {
long double v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithDouble:v];
break;
}
case double_type: {
double v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithDouble:v];
break;
}
case ulong_type: {
unsigned long v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithLong:v];
break;
}
case ulonglong_type: {
unsigned long long v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithLong:v];
break;
}
case uchar_type: {
unsigned char v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithChar:v];
break;
}
case ushort_type: {
unsigned short v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithShort:v];
break;
}
case uint_type: {
unsigned int v;
[invocation getArgument:&v atIndex: j];
val = [ClojureLangRT boxWithInt:v];
break;
}
case bool_type: {
char v;
[invocation getArgument:&v atIndex: j];
val = v == YES ? JavaLangBoolean_get_TRUE__() : JavaLangBoolean_get_FALSE__();
break;
}
case id_type: {
void * v;
[invocation getArgument:&v atIndex:j];
val = v;
break;
}
case cgpoint_type: {
CGPoint v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithCGPoint:v];
break;
}
case nsrange_type: {
NSRange v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithRange:v];
break;
}
case uiedge_type: {
UIEdgeInsets v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithUIEdgeInsets:v];
break;
}
case cgsize_type: {
CGSize v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithCGSize:v];
break;
}
case cgaffinetransform_type: {
CGAffineTransform v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithCGAffineTransform:v];
break;
}
case catransform3d_type: {
CATransform3D v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithCATransform3D:v];
break;
}
case uioffset_type: {
UIOffset v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithUIOffset:v];
break;
}
case cgrect_type: {
CGRect v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithCGRect:v];
break;
}
case pointer_type: {
void* v;
[invocation getArgument:&v atIndex: j];
val = [NSValue valueWithPointer:v];
break;
}
default: @throw [NSException exceptionWithName:@"Error"
reason:[NSString stringWithFormat:@"%@",
[typesa objectAtIndex:n]] userInfo:nil];
}
args = [fconj invokeWithId:args withId:val];
}
id v = [fn applyToWithClojureLangISeq:[ClojureLangRT seqWithId:args]];
void * ret;
switch (retType) {
case void_type: {
id r = nil;
ret = &r;
break;
}
case float_type: {
float o = [ClojureLangRT floatCastWithId:v];
ret = &o;
break;
}
case longlong_type: {
long long o = [ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case long_type: {
long o = (long)[ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case char_type: {
char o = [ClojureLangRT charCastWithId:v];
ret = &o;
break;
}
case short_type: {
short o = [ClojureLangRT shortCastWithId:v];
ret = &o;
break;
}
case int_type: {
int o = [ClojureLangRT intCastWithId:v];
ret = &o;
break;
}
case double_type: {
double o = [ClojureLangRT doubleCastWithId:v];
ret = &o;
break;
}
case longdouble_type: {
long double o = [ClojureLangRT doubleCastWithId:v];
ret = &o;
break;
}
case ulong_type: {
unsigned long o = (unsigned long)[ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case ulonglong_type: {
unsigned long long o = [ClojureLangRT longCastWithId:v];
ret = &o;
break;
}
case uchar_type: {
unsigned char o = [ClojureLangRT charCastWithId:v];
ret = &o;
break;
}
case ushort_type: {
unsigned short o = [ClojureLangRT shortCastWithId:v];
ret = &o;
break;
}
case uint_type: {
unsigned int o = [ClojureLangRT intCastWithId:v];
ret = &o;
break;
}
case bool_type: {
BOOL o = [ClojureLangRT booleanCastWithId:v];
ret = &o;
break;
}
case id_type: {
ret = &v;
break;
}
case cgpoint_type: {
CGPoint o = [((NSValue*) v) CGPointValue];
ret = &o;
break;
}
case nsrange_type: {
NSRange o = [((NSValue*) v) rangeValue];
ret = &o;
break;
}
case uiedge_type: {
UIEdgeInsets o = [((NSValue*) v) UIEdgeInsetsValue];
ret = &o;
break;
}
case cgsize_type: {
CGSize o = [((NSValue*) v) CGSizeValue];
ret = &o;
break;
}
case cgaffinetransform_type: {
CGAffineTransform o = [((NSValue*) v) CGAffineTransformValue];
ret = &o;
break;
}
case catransform3d_type: {
CATransform3D o = [((NSValue*) v) CATransform3DValue];
ret = &o;
break;
}
case uioffset_type: {
UIOffset o = [((NSValue*) v) UIOffsetValue];
ret = &o;
break;
}
case cgrect_type: {
CGRect o = [((NSValue*) v) CGRectValue];
ret = &o;
break;
}
case pointer_type: {
void* o = [((NSValue*) v) pointerValue];
ret = &o;
break;
}
default: {
@throw [NSException exceptionWithName:@"Missing type" reason:[NSString stringWithFormat:@"%c", retType] userInfo:nil];
}
}
if (retType != 'v') {
[invocation setReturnValue:ret];
}
}
id boxValue(void* val, char type) {
id result;
switch (type) {
case void_type: {
return [NSNull null];
}
case float_type: {
return [ClojureLangRT boxWithFloat:*(float*)val];
}
case long_type: {
return [ClojureLangRT boxWithLong:(long)*(long*)val];
}
case longlong_type: {
return [ClojureLangRT boxWithLong:*(long long*)val];
}
case char_type: {
if (*(char*)val == YES) {
return JavaLangBoolean_get_TRUE__();
} else if (*(char*)val == NO) {
return JavaLangBoolean_get_FALSE__();
} else {
return [ClojureLangRT boxWithChar:*(char*)val];
}
}
case short_type: {
return [ClojureLangRT boxWithShort:*(short*)val];
}
case int_type: {
return [ClojureLangRT boxWithInt:*(int*)val];
}
case double_type: {
return [ClojureLangRT boxWithDouble:*(double*)val];
}
case longdouble_type: {
return [ClojureLangRT boxWithDouble:*(long double*)val];
}
case ulong_type: {
return [ClojureLangRT boxWithLong:(unsigned long)*(unsigned long*)val];
}
case ulonglong_type: {
return [ClojureLangRT boxWithLong:(unsigned long long)*(unsigned long long*)val];
}
case uchar_type: {
return [ClojureLangRT boxWithChar:*(unsigned char*)val];
}
case ushort_type: {
return [ClojureLangRT boxWithShort:*(unsigned short*)val];
}
case uint_type: {
return [ClojureLangRT boxWithInt:*(unsigned int*)val];
}
case bool_type: {
return *(char*)val == YES ? JavaLangBoolean_get_TRUE__() : JavaLangBoolean_get_FALSE__();
}
case cgpoint_type: {
return [NSValue valueWithCGPoint:*(CGPoint*)val];
}
case nsrange_type: {
return [NSValue valueWithRange:*(NSRange*)val];
}
case uiedge_type: {
return [NSValue valueWithUIEdgeInsets:*(UIEdgeInsets*)val];
}
case cgsize_type: {
return [NSValue valueWithCGSize:*(CGSize*)val];
}
case cgaffinetransform_type: {
return [NSValue valueWithCGAffineTransform:*(CGAffineTransform*)val];
}
case catransform3d_type: {
return [NSValue valueWithCATransform3D:*(CATransform3D*)val];
}
case uioffset_type: {
return [NSValue valueWithUIOffset:*(UIOffset*)val];
}
case cgrect_type: {
return [NSValue valueWithCGRect:*(CGRect*)val];
}
case pointer_type: {
return [NSValue valueWithPointer:val];
}
default: {
return *(void**)val;
}
}
}
#define make_pointer(e,type)\
type o = e;\
type *p = malloc(sizeof(type));\
*p = o;\
argument_values[n] = p;\
break;\
static id ccallWithFn(void* fn, ClojureLangPersistentVector *types, id args) {
if (![args isKindOfClass:[ClojureLangPersistentVector class]]) {
args = [ClojureLangPersistentVector createWithClojureLangISeq:args];
}
char retType = to_char([types nthWithInt:0]);
void *result_value = malloc_ret(retType);
long count = [(ClojureLangPersistentVector*)args count];
ffi_type **argument_types = (ffi_type **) malloc ((count + 1) * sizeof(ffi_type *));
void **argument_values = (void **) malloc ((count + 1) * sizeof(void *));
for (int n=0; n < count; n++) {
char type = to_char([types nthWithInt:n+1]);
argument_types[n] = ffi_type_for_type(type);
id v = [args nthWithInt: n];
switch (type) {
case void_type: {
make_pointer([NSNull null], id);
}
case float_type: {
make_pointer([ClojureLangRT floatCastWithId:v], float);
}
case longlong_type: {
make_pointer([ClojureLangRT longCastWithId:v], long long);
}
case long_type: {
make_pointer((long)[ClojureLangRT longCastWithId:v], long);
}
case char_type: {
if ([v isKindOfClass:[JavaLangBoolean class]]) {
make_pointer([ClojureLangRT booleanCastWithId:v], BOOL);
} else {
make_pointer([ClojureLangRT charCastWithId:v], char);
}
}
case short_type: {
make_pointer([ClojureLangRT shortCastWithId:v], short);
}
case int_type: {
make_pointer([ClojureLangRT intCastWithId:v], int);
}
case double_type: {
make_pointer([ClojureLangRT doubleCastWithId:v], double);
}
case longdouble_type: {
make_pointer([ClojureLangRT doubleCastWithId:v], long double);
}
case ulong_type: {
make_pointer((unsigned long)[ClojureLangRT longCastWithId:v], unsigned long);
}
case ulonglong_type: {
make_pointer([ClojureLangRT longCastWithId:v], unsigned long long);
}
case uchar_type: {
make_pointer([ClojureLangRT charCastWithId:v], unsigned char);
}
case ushort_type: {
make_pointer([ClojureLangRT shortCastWithId:v], unsigned short);
}
case uint_type: {
make_pointer([ClojureLangRT intCastWithId:v], unsigned int);
}
case bool_type: {
make_pointer([ClojureLangRT booleanCastWithId:v], BOOL);
}
case id_type: {
make_pointer([v isKindOfClass:[WeakRef class]] ? [(WeakRef*)v deref] : v, id);
}
case cgpoint_type: {
make_pointer([((NSValue*) v) CGPointValue], CGPoint);
}
case nsrange_type: {
make_pointer([((NSValue*) v) rangeValue], NSRange);
}
case uiedge_type: {
make_pointer([((NSValue*) v) UIEdgeInsetsValue], UIEdgeInsets);
}
case cgsize_type: {
make_pointer([((NSValue*) v) CGSizeValue], CGSize);
}
case cgaffinetransform_type: {
make_pointer([((NSValue*) v) CGAffineTransformValue], CGAffineTransform);
break;
}
case catransform3d_type: {
make_pointer([((NSValue*) v) CATransform3DValue], CATransform3D);
}
case uioffset_type: {
make_pointer([((NSValue*) v) UIOffsetValue], UIOffset);
}
case cgrect_type: {
make_pointer([((NSValue*) v) CGRectValue], CGRect);
}
case pointer_type: {
if ([v isKindOfClass:[ClojureLangSelector class]]) {
make_pointer(NSSelectorFromString([(ClojureLangSelector*)v getName]), SEL);
} else {
make_pointer([((NSValue*) v) pointerValue], void*);
}
}
default: {
@throw [NSException exceptionWithName:@"Type not found" reason:[NSString stringWithFormat:@"%c", type] userInfo:nil];
}
}
}
ffi_cif c;
ffi_type *result_type = ffi_type_for_type(retType);
int status = ffi_prep_cif(&c, FFI_DEFAULT_ABI, (unsigned int) count, result_type, argument_types);
if (status != FFI_OK) {
NSLog(@"Failed to prepare cif structure");
}
ffi_call(&c, fn, result_value, argument_values);
for (int n=0; n < count; n++) {
free(argument_values[n]);
}
free(argument_types);
free(argument_values);
id result = boxValue(result_value, retType);
free(result_value);
return result;
}
const char* makeSignature(id types) {
BOOL first = YES;
NSString *s = @"";
IOSObjectArray *array = [ClojureLangRT toArrayWithId:types];
long c = [array length];
for (int n = 0; n < c; n++) {
s = [s stringByAppendingString:[NSString stringWithFormat:@"%s", encode_type(to_char([array objectAtIndex:n]))]];
if (first) {
s = [s stringByAppendingString:@"@:"];
first = NO;
}
}
return [s UTF8String];
}
@implementation NSCommon
+(BOOL)cgfloatIsDouble {
#if CGFLOAT_IS_DOUBLE
return YES;
#else
return NO;
#endif
}
+(void)initialize {
assoc = [ClojureLangRT varWithNSString:@"clojure.core" withNSString:@"assoc"];
cons = [ClojureLangRT varWithNSString:@"clojure.core" withNSString:@"cons"];
fconj = [ClojureLangRT varWithNSString:@"clojure.core" withNSString:@"conj"];
global_functions = [NSMutableDictionary new];
register_fn(objc_msgSend);
#ifndef __arm64__
register_fn(objc_msgSend_stret);
#endif
register_fn(CGRectMake);
register_fn(CGPointMake);
register_fn(CGSizeMake);
register_fn(CGVectorMake);
register_fn(UIEdgeInsetsMake);
register_fn(UIEdgeInsetsInsetRect);
register_fn(UIOffsetMake);
register_fn(UIEdgeInsetsEqualToEdgeInsets);
register_fn(UIOffsetEqualToOffset);
}
+(id)ccall:(id)name types:(ClojureLangPersistentVector*)types args:(id)args {
NSValue *val = [global_functions objectForKey:name];
void *fn;
if (val != nil) {
fn = [val pointerValue];
} else {
fn = dlsym(RTLD_DEFAULT, [name UTF8String]);
if (fn == nil) {
@throw [NSException exceptionWithName:@"Function not found" reason:name userInfo:nil];
}
[global_functions setObject:[NSValue valueWithPointer:fn] forKey:name];
}
return ccallWithFn(fn, types, args);
}
#if TARGET_CPU_X86
#define dispatch_fastf(params, ...) \
case float_type: { \
float r = objc_msgSend_fpret(object, sel, ##__VA_ARGS__); \
return [[[JavaLangFloat alloc] initWithFloat:r] autorelease]; \
} \
case double_type: { \
double r = objc_msgSend_fpret(object, sel, ##__VA_ARGS__); \
return [[[JavaLangDouble alloc] initWithDouble:r] autorelease]; \
} \
case longdouble_type: { \
long double r = objc_msgSend_fpret(object, sel, ##__VA_ARGS__); \
return [[[JavaLangDouble alloc] initWithDouble:r] autorelease]; \
}
#elif TARGET_CPU_X86_64
#define dispatch_fastf(params, ...) \
case float_type: { \
return [ClojureLangRT boxWithFloat:((float(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
}\
case double_type: { \
return [ClojureLangRT boxWithDouble:((double(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
}\
case longdouble_type: { \
return [ClojureLangRT boxWithDouble:((long double(*)params)objc_msgSend_fpret)(object, sel, ##__VA_ARGS__)]; \
}
#else
#define dispatch_fastf(params, ...) \
case float_type: { \
void* val = objc_msgSend(object, sel, ##__VA_ARGS__); \
return [ClojureLangRT boxWithFloat:*(float*)&val]; \
}\
case double_type: { \
return [ClojureLangRT boxWithDouble:((double(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
}\
case longdouble_type: { \
return [ClojureLangRT boxWithDouble:((long double(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
}
#endif
#define dispatch_fast(params, ...) \
switch (ret) { \
case void_type: { \
objc_msgSend(object, sel, ##__VA_ARGS__); \
return nil;\
} \
case int_type: { \
return [ClojureLangRT boxWithInt:((int(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case bool_type: { \
return ((BOOL(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__) == YES ? JavaLangBoolean_get_TRUE__() : JavaLangBoolean_get_FALSE__(); \
} \
case id_type: { \
return ((id(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__); \
} \
case pointer_type: { \
return [NSValue valueWithPointer:((void*(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)];\
} \
case longlong_type: { \
return [ClojureLangRT boxWithLong:((long long(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case long_type: { \
return [ClojureLangRT boxWithLong:((long(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case char_type: { \
char charv = ((char(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__); \
return charv == YES ? JavaLangBoolean_get_TRUE__() : (charv == NO ? JavaLangBoolean_get_FALSE__() : [ClojureLangRT boxWithChar:charv]); \
} \
case short_type: { \
return [ClojureLangRT boxWithShort:((short(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case ulong_type: { \
return [ClojureLangRT boxWithLong:((unsigned long(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case ulonglong_type: { \
return [ClojureLangRT boxWithLong:((unsigned long long(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case uchar_type: { \
return [ClojureLangRT boxWithChar:((unsigned char(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case ushort_type: { \
return [ClojureLangRT boxWithShort:((unsigned short(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case uint_type: { \
return [ClojureLangRT boxWithInt:((unsigned int(*)params)objc_msgSend)(object, sel, ##__VA_ARGS__)]; \
} \
case cgrect_type: { \
return [NSValue valueWithCGRect:((CGRect(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case cgsize_type: {\
return [NSValue valueWithCGSize:((CGSize(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case cgpoint_type: {\
return [NSValue valueWithCGPoint:((CGPoint(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case nsrange_type: {\
return [NSValue valueWithRange:((NSRange(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case uiedge_type: {\
return [NSValue valueWithUIEdgeInsets:((UIEdgeInsets(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case cgaffinetransform_type: {\
return [NSValue valueWithCGAffineTransform:((CGAffineTransform(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case catransform3d_type: {\
return [NSValue valueWithCATransform3D:((CATransform3D(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
case uioffset_type: {\
return [NSValue valueWithUIOffset:((UIOffset(*)params)fun)(object, sel, ##__VA_ARGS__)];\
}\
dispatch_fastf(params, ##__VA_ARGS__) \
}\
+ (id) invokeSel:(id)object withSelector:(NSString*)selector withArgs:(id)arguments {
if ([object isKindOfClass:[NSString class]] && ClojureLangRemoteRepl_get_connected_()) {
if (([selector isEqualToString:@"autorelease"])) {
return object;
} else if ([selector isEqualToString:@"release"]) {
return nil;
}
}
SEL sel = NSSelectorFromString(selector);
NSMethodSignature *sig = [([object isKindOfClass:[WeakRef class]] ? [(WeakRef*)object deref] : object) methodSignatureForSelector:sel];
if (sig == nil) {
@throw([NSException exceptionWithName:@"Error invoking objc method. Selector not found" reason:selector userInfo:nil]);
}
char ret = signatureToType([sig methodReturnType]);
#ifndef __arm64__
bool stret = use_stret(ret);
#else
bool stret = NO;
#endif
object = [object isKindOfClass:[WeakRef class]] ? [object deref] : object;
#ifndef __arm64__
void *fun;
if (stret) {
fun = objc_msgSend_stret;
} else {
#if TARGET_CPU_X86
if (ret == float_type || ret == double_type || ret == longdouble_type) {
fun = objc_msgSend_fpret;
} else {
#endif
fun = objc_msgSend;
#if TARGET_CPU_X86
}
#endif
}
#else
void *fun;
if (ret == longdouble_type) {
fun = objc_msgSend_fpret;
} else {
fun = objc_msgSend;
}
#endif
// TODO: ulonglong return fails with dispatch_fast
if (ret != ulonglong_type) {
switch ([arguments count]) {
case 0: {
dispatch_fast((id, SEL));
break;
}
case 1: {
id v = [arguments first];
switch (signatureToType([sig getArgumentTypeAtIndex:2])) {
case id_type: {
dispatch_fast((id, SEL, id), (id)([v isKindOfClass:[WeakRef class]] ? [(WeakRef*)v deref] : v));
}
case int_type: {
dispatch_fast((id, SEL, int), [ClojureLangRT intCastWithId:v]);
}
case uint_type: {
dispatch_fast((id, SEL, unsigned int), [ClojureLangRT intCastWithId:v]);
}
case ulonglong_type: {
dispatch_fast((id, SEL, unsigned long long), (unsigned long long)[ClojureLangRT longCastWithId:v]);
}
case ulong_type: {
dispatch_fast((id, SEL, unsigned long), (unsigned long)[ClojureLangRT longCastWithId:v]);
}
case long_type: {
dispatch_fast((id, SEL, long), (long)[ClojureLangRT longCastWithId:v]);
}
case longlong_type: {
dispatch_fast((id, SEL, long long), (long long)[ClojureLangRT longCastWithId:v]);
}
case float_type: {
float f = [ClojureLangRT floatCastWithId:v];
// float required special handling
dispatch_fast((id, SEL, int), *(int*)&f);
}
case uchar_type: {
dispatch_fast((id, SEL, unsigned char), (unsigned char)[ClojureLangRT charCastWithId:v]);
}
case char_type: {
dispatch_fast((id, SEL, char), v == JavaLangBoolean_get_TRUE__() ? YES :
(v == JavaLangBoolean_get_FALSE__() ? NO : [ClojureLangRT charCastWithId:v]));
}
case ushort_type: {
dispatch_fast((id, SEL, unsigned short), (unsigned short)[ClojureLangRT shortCastWithId:v]);
}
case short_type: {
dispatch_fast((id, SEL, short), [ClojureLangRT shortCastWithId:v]);
}
case double_type: {
dispatch_fast((id, SEL, double), [ClojureLangRT doubleCastWithId:v]);
}
case longdouble_type: {
dispatch_fast((id, SEL, long double), (long double)[ClojureLangRT doubleCastWithId:v]);
}
case bool_type: {
dispatch_fast((id, SEL, bool), [ClojureLangRT booleanCastWithId:v]);
}
case pointer_type: {
dispatch_fast((id, SEL, void*), [v isKindOfClass:[ClojureLangSelector class]] ? NSSelectorFromString ([(ClojureLangSelector*)v getName]) : [(NSValue*)v pointerValue]);
}
case cgpoint_type: {
dispatch_fast((id, SEL, CGPoint), [(NSValue*)v CGPointValue]);
}
case nsrange_type: {
dispatch_fast((id, SEL, NSRange), [(NSValue*)v rangeValue]);
}
case uiedge_type: {
dispatch_fast((id, SEL, UIEdgeInsets), [(NSValue*)v UIEdgeInsetsValue]);
}
case cgsize_type: {
dispatch_fast((id, SEL, CGSize), [(NSValue*)v CGSizeValue]);
}
case cgaffinetransform_type: {
dispatch_fast((id, SEL, CGAffineTransform), [(NSValue*)v CGAffineTransformValue]);
}
case catransform3d_type: {
dispatch_fast((id, SEL, CATransform3D), [(NSValue*)v CATransform3DValue]);
}
case uioffset_type: {
dispatch_fast((id, SEL, UIOffset), [(NSValue*)v UIOffsetValue]);
}
case cgrect_type: {
dispatch_fast((id, SEL, CGRect), [(NSValue*)v CGRectValue]);
}
}
break;
}
}
}
if ([selector isEqualToString:@"ccall:types:args:"]) {
return [NSCommon ccall:[ClojureLangRT nthFromWithId:arguments withInt:0] types:[ClojureLangRT nthFromWithId:arguments withInt:1] args:[ClojureLangRT nthFromWithId:arguments withInt:2]];
}
return ccallWithFn(fun, signaturesToTypes(sig, NO), [cons invokeWithId:object withId:[cons invokeWithId:[NSValue valueWithPointer:sel] withId:arguments]]);
}
+ (id) invokeSuperSel:(id)object withDispatchClass:(id)clazz withSelector:(NSString*)selector
withArgs:(id)arguments {
SEL sel = NSSelectorFromString(selector);
clazz = clazz == nil ? [object superclass] : [NSClassFromString(clazz) superclass];
if (classIsDynamic(clazz)) {
objc_setAssociatedObject(object, "__dispatch_class__", clazz, 1);
}
struct objc_super superData = {object, clazz};
NSMethodSignature *sig = [object methodSignatureForSelector:sel];
if (sig == nil) {
@throw([NSException exceptionWithName:@"Error invoking superclass objc method. Selector not found" reason:selector userInfo:nil]);
}
id types = [assoc invokeWithId:signaturesToTypes(sig, NO) withId:[[[JavaLangInteger alloc] initWithInt:1] autorelease] withId:[[[JavaLangCharacter alloc] initWithChar:pointer_type] autorelease]];
id args = [cons invokeWithId:[NSValue valueWithPointer:sel] withId:arguments];
args = [cons invokeWithId:[NSValue valueWithPointer:(void*)&superData] withId:args];
#ifndef __arm64__
void *s;
if (use_stret(signatureToType([sig methodReturnType]))) {
s = objc_msgSendSuper_stret;
} else {
s = objc_msgSendSuper;
}
#else
void *s = FFI_FN(objc_msgSendSuper);
#endif
return ccallWithFn(s, types, args);
}
+ (id) invokeFun:(NSString*)fun withSelf:(id)object withSelector:(NSString*)selector withArgs:(id)arguments {
SEL sel = NSSelectorFromString(selector);
NSMethodSignature *sig = [([object isKindOfClass:[WeakRef class]] ? [(WeakRef*)object deref] : object) methodSignatureForSelector:sel];
if (sig == nil) {
@throw([NSException exceptionWithName:@"Error invoking objc method. Selector not found" reason:selector userInfo:nil]);
}
id args = [cons invokeWithId:[NSValue valueWithPointer:NSSelectorFromString(selector)] withId:arguments];
args = [cons invokeWithId:object withId:args];
return [NSCommon ccall:fun types:signaturesToTypes(sig, NO) args:args];
}
@end
================================================
FILE: src/objc/NSProxyImpl.h
================================================
#import
#import "clojure/lang/APersistentMap.h"
@interface NSProxyImpl: NSObject
- (id) initWithClass:(NSString*)clazz map:(ClojureLangAPersistentMap*) m;
@end
================================================
FILE: src/objc/NSProxyImpl.m
================================================
#import "NSProxyImpl.h"
#import "clojure/lang/AFn.h"
#import "clojure/lang/RT.h"
#import "clojure/lang/PersistentVector.h"
#import "clojure/lang/Selector.h"
#import "clojure/lang/Var.h"
#import "NSCommon.h"
#import
@implementation NSProxyImpl {
ClojureLangAPersistentMap *map;
Class clazz;
id instance;
}
- (id) initWithClass:(NSString*)c map:(ClojureLangAPersistentMap*) m
{
self = [super init];
if (self) {
map = [m retain];
if (c == nil) {
clazz = [NSObject class];
} else {
clazz = NSClassFromString(c);
}
instance = [[clazz alloc] init];
}
return self;
}
- (void) forwardInvocation:(NSInvocation *)invocation
{
id r = [map valAtWithId:NSStringFromSelector([invocation selector])];
if (r == nil) {
[invocation invokeWithTarget:instance];
} else {
callWithInvocation(invocation, self, [ClojureLangRT firstWithId:r], [ClojureLangRT secondWithId:r]);
}
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
if ([NSStringFromSelector(sel) isEqualToString: @"initWithClass:map:"] ||
[NSStringFromSelector(sel) isEqualToString: @"class"] ||
[NSStringFromSelector(sel) isEqualToString: @"alloc"] ||
[NSStringFromSelector(sel) isEqualToString: @"retain"] ||
[NSStringFromSelector(sel) isEqualToString: @"retainCount"] ||
[NSStringFromSelector(sel) isEqualToString: @"release"] ||
[NSStringFromSelector(sel) isEqualToString: @"dealloc"]) {
return [NSProxyImpl instanceMethodSignatureForSelector:sel];
}
id r = [map valAtWithId:NSStringFromSelector(sel)];
if (r == nil) {
return [clazz instanceMethodSignatureForSelector:sel];
} else {
return [NSMethodSignature signatureWithObjCTypes:makeSignature([ClojureLangRT firstWithId:r])];
}
}
-(BOOL)respondsToSelector:(SEL)sel {
if ([map valAtWithId:NSStringFromSelector(sel)] != nil || [instance respondsToSelector:sel]) {
return YES;
}
return NO;
}
-(id)retain {
id r = [map valAtWithId:@"retain"];
if (r != nil) {
[(ClojureLangAFn*)[ClojureLangRT secondWithId:r] invokeWithId:self];
}
return [super retain];
}
-(oneway void)release {
id r = [map valAtWithId:@"release"];
if (r != nil) {
[(ClojureLangAFn*)[ClojureLangRT secondWithId:r] invokeWithId:self];
}
[super release];
}
-(NSUInteger)retainCount {
id r = [map valAtWithId:@"retainCount"];
if (r != nil) {
[(ClojureLangAFn*)[ClojureLangRT secondWithId:r] invokeWithId:self];
}
return [super retainCount];
}
-(void)dealloc {
id r = [map valAtWithId:@"dealloc"];
if (r != nil) {
[(ClojureLangAFn*)[ClojureLangRT secondWithId:r] invokeWithId:self];
}
[instance release];
[map release];
[super dealloc];
}
-(NSString*)description {
id r = [map valAtWithId:@"description"];
if (r != nil) {
return [(ClojureLangAFn*)[ClojureLangRT secondWithId:r] invokeWithId:self];
} else {
return [instance description];
}
}
@end
================================================
FILE: src/objc/NSSocketImpl.h
================================================
//
// NSSocketImpl.h
// sample
//
// Created by Gal Dolber on 2/1/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import
@interface NSSocketImpl : NSObject
- (id) initWithHost:(NSString*)hostName withPort:(NSString*)portNum;
- (id) read;
- (void) println: (NSString*) s;
-(void)close;
@end
================================================
FILE: src/objc/NSSocketImpl.m
================================================
//
// NSSocketImpl.m
// sample
//
// Created by Gal Dolber on 2/1/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import "NSSocketImpl.h"
#import "clojure/lang/RT.h"
#import "clojure/lang/Var.h"
#import "clojure/lang/Selector.h"
#import "clojure/lang/ISeq.h"
#import "clojure/lang/ObjC.h"
#import "clojure/lang/Selector.h"
#import "Cst502Socket.h"
#import
@implementation NSSocketImpl {
Cst502ClientSocket* cs;
}
- (id) initWithHost:(NSString*)hostName withPort:(NSString*)portNum {
self = [super init];
if (self) {
cs = [[Cst502ClientSocket alloc] initWithHost: hostName
portNumber: portNum];
[self reconnect];
}
return self;
}
- (void) reconnect {
while(![cs connect]) {
NSLog(@"Error connecting... will try again in 5 second");
[NSThread sleepForTimeInterval:5];
}
}
- (id) read {
fflush (stdout);
char *buf = malloc(12);
NSString *a = [[cs receiveBytes:buf maxBytes:12 beginAt:0] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
/*if ([@"" isEqualToString:a]) {
[self reconnect];
return [self read];
}*/
int size = [a intValue] + 2;
free(buf);
buf = malloc(size);
a = [cs receiveBytes:buf maxBytes:size beginAt:0];
free(buf);
return a;
}
- (void) println: (NSString*) s {
[cs sendString: s];
fflush (stdout);
}
-(void)dealloc {
[cs release];
[super dealloc];
}
-(void)close {
[cs close];
}
@end
================================================
FILE: src/objc/NSTypeImpl.h
================================================
//
// NSTypeImpl.h
// sample
//
// Created by Gal Dolber on 2/4/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import
#import "clojure/lang/APersistentMap.h"
#import "clojure/lang/PersistentVector.h"
#import "clojure/lang/PersistentHashMap.h"
@interface NSTypeImpl : NSObject
+(Class) makeClassWithName:(NSString*)name superclass:(NSString*)s map:(ClojureLangAPersistentMap*)m;
@end
================================================
FILE: src/objc/NSTypeImpl.m
================================================
//
// NSTypeImpl.m
// sample
//
// Created by Gal Dolber on 2/4/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import "NSTypeImpl.h"
#import "objc/runtime.h"
#import "objc/message.h"
#import "clojure/lang/Atom.h"
#import "java/lang/RuntimeException.h"
#import "java/lang/Character.h"
#import "clojure/lang/RT.h"
#import "NSCommon.h"
#import
#define va_arg_p(type)\
{\
type v = va_arg(ap, type); \
type* vp = malloc(sizeof(type)); \
memcpy(vp, &v, sizeof(type)); \
args[n] = vp; \
break;\
}\
#define dispatch_args(self, sel, type) \
va_list ap; \
va_start(ap, sel); \
Class dispatchClass = objc_getAssociatedObject(self, "__dispatch_class__"); \
objc_setAssociatedObject(self, "__dispatch_class__", nil, 0); \
if (dispatchClass == nil) { \
dispatchClass = [self class]; \
} \
id o = [ClojureLangRT getWithId:[dynamicClasses deref] withId:NSStringFromClass(dispatchClass)]; \
id pair = [ClojureLangRT getWithId:o withId:NSStringFromSelector(sel)]; \
id fn = [ClojureLangRT secondWithId:pair]; \
while (fn == nil) { \
dispatchClass = [dispatchClass superclass]; \
o = [ClojureLangRT getWithId:[dynamicClasses deref] withId:NSStringFromClass(dispatchClass)]; \
pair = [ClojureLangRT getWithId:o withId:NSStringFromSelector(sel)]; \
fn = [ClojureLangRT secondWithId:pair]; \
if (o == nil) { \
break; \
} \
} \
if (fn == nil) { \
@throw [NSException exceptionWithName:NSStringFromClass(dispatchClass) reason:NSStringFromSelector(sel) userInfo:nil]; \
} \
id types = [ClojureLangRT firstWithId:pair];\
id sig;\
if (types == nil) { \
sig = [self methodSignatureForSelector:sel]; \
types = signaturesToTypes(sig, YES); \
} else {\
sig = [NSMethodSignature signatureWithObjCTypes:makeSignature(types)];\
}\
id retType = [ClojureLangRT firstWithId:types]; \
IOSObjectArray *typesa = [ClojureLangRT toArrayWithId:[ClojureLangRT nextWithId:types]]; \
long c = [typesa length]; \
void **args = (void **) malloc ((c + 1) * sizeof(void *)); \
for (int n = 0; n < c; n++) { \
switch (to_char([typesa objectAtIndex:n])) { \
case float_type: va_arg_p(double)\
case longlong_type: va_arg_p(long long)\
case long_type: va_arg_p(long)\
case char_type: va_arg_p(int)\
case short_type: va_arg_p(int)\
case int_type: va_arg_p(int)\
case double_type: va_arg_p(double)\
case ulong_type: va_arg_p(unsigned long)\
case ulonglong_type: va_arg_p(unsigned long long)\
case uchar_type: va_arg_p(int)\
case ushort_type: va_arg_p(int)\
case uint_type: va_arg_p(unsigned int)\
case bool_type: va_arg_p(int)\
case id_type: va_arg_p(id)\
case cgpoint_type: va_arg_p(CGPoint)\
case nsrange_type: va_arg_p(NSRange)\
case uiedge_type: va_arg_p(UIEdgeInsets)\
case cgsize_type: va_arg_p(CGSize)\
case cgaffinetransform_type: va_arg_p(CGAffineTransform)\
case catransform3d_type: va_arg_p(CATransform3D)\
case uioffset_type: va_arg_p(UIOffset)\
case cgrect_type: va_arg_p(CGRect)\
case pointer_type: va_arg_p(void*)\
} \
} \
va_end(ap); \
type* rp = callWithArgs(args, self, [ClojureLangRT consWithId:retType withId:types], fn); \
#define dispatch_args_r(self, sel, type)\
dispatch_args(self, sel, type);\
type r = *rp; \
free(args); \
return r;\
void dispatch_void(id self, SEL sel, ...) {
dispatch_args(self, sel, void);
free(args);
}
float dispatch_float(id self, SEL sel, ...) {
dispatch_args_r(self, sel, float);
}
long long dispatch_longlong(id self, SEL sel, ...) {
dispatch_args_r(self, sel, long long);
}
long dispatch_long(id self, SEL sel, ...) {
dispatch_args_r(self, sel, long);
}
char dispatch_char(id self, SEL sel, ...) {
dispatch_args_r(self, sel, char);
}
short dispatch_short(id self, SEL sel, ...) {
dispatch_args_r(self, sel, short);
}
int dispatch_int(id self, SEL sel, ...) {
dispatch_args_r(self, sel, int);
}
double dispatch_double(id self, SEL sel, ...) {
dispatch_args_r(self, sel, double);
}
unsigned long long dispatch_unsigned_longlong(id self, SEL sel, ...) {
dispatch_args_r(self, sel, unsigned long long);
}
unsigned long dispatch_unsigned_long(id self, SEL sel, ...) {
dispatch_args_r(self, sel, unsigned long);
}
unsigned char dispatch_unsigned_char(id self, SEL sel, ...) {
dispatch_args_r(self, sel, unsigned char);
}
unsigned short dispatch_unsigned_short(id self, SEL sel, ...) {
dispatch_args_r(self, sel, unsigned short);
}
unsigned int dispatch_unsigned_int(id self, SEL sel, ...) {
dispatch_args_r(self, sel, unsigned int);
}
bool dispatch_bool(id self, SEL sel, ...) {
dispatch_args_r(self, sel, BOOL);
}
CGPoint dispatch_CGPoint(id self, SEL sel, ...) {
dispatch_args_r(self, sel, CGPoint);
}
NSRange dispatch_NSRange(id self, SEL sel, ...) {
dispatch_args_r(self, sel, NSRange);
}
UIEdgeInsets dispatch_UIEdgeInsets(id self, SEL sel, ...) {
dispatch_args_r(self, sel, UIEdgeInsets);
}
CGSize dispatch_CGSize(id self, SEL sel, ...) {
dispatch_args_r(self, sel, CGSize);
}
CGAffineTransform dispatch_CGAffineTransform(id self, SEL sel, ...) {
dispatch_args_r(self, sel, CGAffineTransform);
}
CATransform3D dispatch_CATransform3D(id self, SEL sel, ...) {
dispatch_args_r(self, sel, CATransform3D);
}
UIOffset dispatch_UIOffset(id self, SEL sel, ...) {
dispatch_args_r(self, sel, UIOffset);
}
CGRect dispatch_CGRect(id self, SEL sel, ...) {
dispatch_args_r(self, sel, CGRect);
}
id dispatch_id(id self, SEL sel, ...) {
dispatch_args_r(self, sel, id);
}
void* dispatch_pointer(id self, SEL sel, ...) {
dispatch_args_r(self, sel, void*);
}
IMP getDispatch(char c) {
switch (c) {
case void_type: return (IMP)dispatch_void;
case float_type: return (IMP)dispatch_float;
case long_type: return (IMP)dispatch_long;
case longlong_type: return (IMP)dispatch_longlong;
case char_type: return (IMP)dispatch_char;
case short_type: return (IMP)dispatch_short;
case int_type: return (IMP)dispatch_int;
case double_type: return (IMP)dispatch_double;
case ulong_type: return (IMP)dispatch_unsigned_long;
case ulonglong_type: return (IMP)dispatch_unsigned_longlong;
case uchar_type: return (IMP)dispatch_unsigned_char;
case ushort_type: return (IMP)dispatch_unsigned_short;
case uint_type: return (IMP)dispatch_unsigned_int;
case bool_type: return (IMP)dispatch_bool;
case cgpoint_type: return (IMP)dispatch_CGPoint;
case nsrange_type: return (IMP)dispatch_NSRange;
case uiedge_type: return (IMP)dispatch_UIEdgeInsets;
case cgsize_type: return (IMP)dispatch_CGSize;
case cgaffinetransform_type: return (IMP)dispatch_CGAffineTransform;
case catransform3d_type: return (IMP)dispatch_CATransform3D;
case uioffset_type: return (IMP)dispatch_UIOffset;
case cgrect_type: return (IMP)dispatch_CGRect;
case pointer_type: return (IMP)dispatch_pointer;
}
return (IMP)dispatch_id;
}
@implementation NSTypeImpl
+(void)initialize {
dynamicClasses = [[ClojureLangAtom alloc] initWithId:ClojureLangPersistentHashMap_get_EMPTY_()];
}
+(Class) makeClassWithName:(NSString*)name superclass:(NSString*)s map:(ClojureLangAPersistentMap*)m {
[dynamicClasses swapWithClojureLangIFn:[[ClojureLangRT varWithNSString:@"clojure.core" withNSString:@"assoc"] deref]
withId:name withId:m];
Class superc = NSClassFromString(s);
Class clazz = objc_allocateClassPair(superc, [name UTF8String], 0);
BOOL wasDefined = NO;
if (clazz == nil) {
wasDefined = YES;
clazz = NSClassFromString(name);
}
id seq = [m seq];
while (seq != nil) {
id f = [ClojureLangRT firstWithId:seq];
SEL sel = NSSelectorFromString([ClojureLangRT firstWithId:f]);
id types = [ClojureLangRT firstWithId:[ClojureLangRT secondWithId:f]];
void * d;
const char * enc;
if (types == nil) {
Method method = class_getInstanceMethod(superc, sel);
char ret[256];
method_getReturnType(method, ret, 256);
d = getDispatch(signatureToType(ret));
enc = method_getTypeEncoding(method);
} else {
id r = [ClojureLangRT firstWithId:types];
d = getDispatch(to_char(r));
enc = makeSignature(types);
}
class_replaceMethod(clazz, sel, d, enc);
seq = [ClojureLangRT nextWithId:seq];
}
if (!wasDefined) {
objc_registerClassPair(clazz);
}
return clazz;
}
@end
================================================
FILE: src/objc/ReplClient.h
================================================
//
// ReplClient.h
// mpos
//
// Created by Gal Dolber on 4/12/14.
// Copyright (c) 2014 zuldi. All rights reserved.
//
#import
@interface ReplClient : NSObject
+(void) connect: (NSString*) host;
+(id) callRemote:(id)sel args:(id)args;
@end
================================================
FILE: src/objc/ReplClient.m
================================================
//
// ReplClient.m
// mpos
//
// Created by Gal Dolber on 4/12/14.
// Copyright (c) 2014 zuldi. All rights reserved.
//
#import "ReplClient.h"
#import "clojure/lang/RemoteRepl.h"
#import "clojure/lang/RT.h"
#import "clojure/lang/Keyword.h"
#import "clojure/lang/AFn.h"
#import "clojure/lang/PersistentHashMap.h"
#import "clojure/lang/Var.h"
#import "clojure/lang/Atom.h"
#import "NSSocketImpl.h"
#import "clojure/core_keyword.h"
#import "clojure/core_pr_str.h"
#import "clojure/core_vector.h"
#import "clojure/core_vec.h"
#import "clojure/core_assoc.h"
#import "clojure/core_dissoc.h"
static NSSocketImpl *nssocket;
static NSSocketImpl *nssocket2;
static ClojureLangAtom *responses;
@implementation ReplClient
+(void) processCall2:(NSSocketImpl*)socket msg:(id)msg {
@try {
id i = [ClojureLangRT firstWithId:msg];
id s = [ClojureLangRT secondWithId:msg];
id args = [ClojureLangRT thirdWithId:msg];
id r = [(ClojureLangAFn*)s applyToWithClojureLangISeq:[ClojureLangRT seqWithId:args]];
[responses swapWithClojureLangIFn:Clojurecore_assoc_get_VAR_() withId:i withId:r];
[socket println:[Clojurecore_pr_str_get_VAR_() invokeWithId:[Clojurecore_vector_get_VAR_() invokeWithId:i withId:r]]];
}
@catch (NSException *exception) {
[socket println:[Clojurecore_pr_str_get_VAR_() invokeWithId:[NSString stringWithFormat:@"%@",[exception callStackSymbols]]]];
NSLog(@"%@", exception);
}
}
+(void) processCall:(NSSocketImpl*)socket msg:(id)msg {
if ([ReplClient maybeRetry:socket msg:msg]) {
return;
}
id runInMain = [ClojureLangRT firstWithId:msg];
msg = [ClojureLangRT nextWithId:msg];
if ([ClojureLangRT booleanCastWithId:runInMain]) {
if ([NSThread isMainThread]) {
[ReplClient processCall2:socket msg:msg];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[ReplClient processCall2:socket msg:msg];
});
}
} else {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[ReplClient processCall2:socket msg:msg];
});
}
}
+(BOOL) maybeRetry:(NSSocketImpl*)socket msg:(id) msg {
if ([[ClojureLangRT firstWithId:msg] isEqual:[ClojureLangKeyword internWithNSString:@"retry"]]) {
id i = [ClojureLangRT secondWithId:msg];
id r = [[responses deref] valAtWithId:i];
[socket println:[Clojurecore_pr_str_get_VAR_() invokeWithId:[Clojurecore_vector_get_VAR_() invokeWithId:i withId:r]]];
return YES;
} else {
return NO;
}
}
+(void) connect: (NSString*) host {
responses = [[ClojureLangAtom alloc] initWithId:ClojureLangPersistentHashMap_get_EMPTY_()];
nssocket = [[NSSocketImpl alloc] initWithHost:host withPort:@"35813"];
nssocket2 = [[NSSocketImpl alloc] initWithHost:host withPort:@"35814"];
[ClojureLangRemoteRepl setConnectedWithBoolean:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (true) {
[ReplClient processCall:nssocket msg:[ClojureLangRT readStringWithNSString:[nssocket read]]];
}
});
}
+(id) callRemote:(id)sel args:(id)args {
args = [Clojurecore_vec_get_VAR_() invokeWithId:args];
id i = [[ReplClient randomUUID] description];
[nssocket2 println:[Clojurecore_pr_str_get_VAR_()
invokeWithId:[Clojurecore_vector_get_VAR_()
invokeWithId:[ClojureLangRT boxWithBoolean:[NSThread isMainThread]] withId:i withId:sel withId:args]]];
while (true) {
id v = [ClojureLangRT readStringWithNSString:[nssocket2 read]];
if (![ReplClient maybeRetry:nssocket2 msg:v]) {
if ([ClojureLangRT countWithId:v] == 4) {
[ReplClient processCall:nssocket2 msg:v];
} else {
return [ClojureLangRT secondWithId:v];
}
}
}
}
+ (NSString *)randomUUID {
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
NSString *uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid);
return uuidString;
}
@end
================================================
FILE: src/objc/WeakRef.h
================================================
//
// WeakRef.h
// sample
//
// Created by Gal Dolber on 2/14/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import
@interface WeakRef : NSObject
-(id)initWith:(id)o;
-(id)deref;
+(WeakRef*)from:(id)o;
-(BOOL)isEqual:(id)f;
@end
================================================
FILE: src/objc/WeakRef.m
================================================
//
// WeakRef.m
// sample
//
// Created by Gal Dolber on 2/14/14.
// Copyright (c) 2014 clojure-objc. All rights reserved.
//
#import "WeakRef.h"
#import "clojure/lang/RT.h"
#import "clojure/lang/Var.h"
#import "clojure/lang/PersistentHashMap.h"
#import "clojure/lang/Atom.h"
@implementation WeakRef {
NSValue *val;
}
-(id)initWith:(id)o {
self = [super init];
if (self) {
val = [[NSValue valueWithNonretainedObject:o] retain];
}
return self;
}
-(id)deref {
return [val nonretainedObjectValue];
}
+(WeakRef*)from:(id)o {
return [[[WeakRef alloc] initWith:o] autorelease];
}
- (BOOL)isEqual:(id)f {
if (f != nil && [f isKindOfClass:[WeakRef class]]) {
return [[(WeakRef*)f deref] isEqual:[self deref]];
}
return NO;
}
-(NSString *)description {
return [[val nonretainedObjectValue] description];
}
-(void)dealloc {
[val release];
[super dealloc];
}
@end
================================================
FILE: src/resources/clojure/version.properties
================================================
version=${version}
================================================
FILE: src/script/run_test.clj
================================================
(System/setProperty "java.awt.headless" "true")
(require
'[clojure.test :as test]
'[clojure.tools.namespace.find :as ns])
(def namespaces (ns/find-namespaces-in-dir (java.io.File. "test")))
(doseq [ns namespaces] (require ns))
(let [summary (apply test/run-tests namespaces)]
(System/exit (if (test/successful? summary) 0 -1)))
================================================
FILE: src/script/run_test_generative.clj
================================================
(System/setProperty "java.awt.headless" "true")
(when-not (System/getProperty "clojure.test.generative.msec")
(System/setProperty "clojure.test.generative.msec" "60000"))
(require '[clojure.test.generative.runner :as runner])
(runner/-main "test")
================================================
FILE: test/clojure/test_clojure/agents.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Shawn Hoover
(ns clojure.test-clojure.agents
(:use clojure.test)
(:import [java.util.concurrent CountDownLatch TimeUnit]))
;; tests are fragile. If wait fails, could indicate that
;; build box is thrashing.
(def fragile-wait 1000)
(deftest handle-all-throwables-during-agent-actions
;; Bug fixed in r1198; previously hung Clojure or didn't report agent errors
;; after OutOfMemoryError, yet wouldn't execute new actions.
(let [agt (agent nil)]
(send agt (fn [state] (throw (Throwable. "just testing Throwables"))))
(try
;; Let the action finish; eat the "agent has errors" error that bubbles up
(await-for fragile-wait agt)
(catch RuntimeException _))
(is (instance? Throwable (first (agent-errors agt))))
(is (= 1 (count (agent-errors agt))))
;; And now send an action that should work
(clear-agent-errors agt)
(is (= nil @agt))
(send agt nil?)
(is (true? (await-for fragile-wait agt)))
(is (true? @agt))))
(deftest default-modes
(is (= :fail (error-mode (agent nil))))
(is (= :continue (error-mode (agent nil :error-handler println)))))
(deftest continue-handler
(let [err (atom nil)
agt (agent 0 :error-mode :continue :error-handler #(reset! err %&))]
(send agt /)
(is (true? (await-for fragile-wait agt)))
(is (= 0 @agt))
(is (nil? (agent-error agt)))
(is (= agt (first @err)))
(is (true? (instance? ArithmeticException (second @err))))))
;; TODO: make these tests deterministic (i.e. not sleep and hope)
#_(deftest fail-handler
(let [err (atom nil)
agt (agent 0 :error-mode :fail :error-handler #(reset! err %&))]
(send agt /)
(Thread/sleep 100)
(is (true? (instance? ArithmeticException (agent-error agt))))
(is (= 0 @agt))
(is (= agt (first @err)))
(is (true? (instance? ArithmeticException (second @err))))
(is (thrown? RuntimeException (send agt inc)))))
(deftest can-send-from-error-handler-before-popping-action-that-caused-error
(let [latch (CountDownLatch. 1)
target-agent (agent :before-error)
handler (fn [agt err]
(send target-agent
(fn [_] (.countDown latch))))
failing-agent (agent nil :error-handler handler)]
(send failing-agent (fn [_] (throw (RuntimeException.))))
(is (.await latch 10 TimeUnit/SECONDS))))
(deftest can-send-to-self-from-error-handler-before-popping-action-that-caused-error
(let [latch (CountDownLatch. 1)
handler (fn [agt err]
(send *agent*
(fn [_] (.countDown latch))))
failing-agent (agent nil :error-handler handler)]
(send failing-agent (fn [_] (throw (RuntimeException.))))
(is (.await latch 10 TimeUnit/SECONDS))))
#_(deftest restart-no-clear
(let [p (promise)
agt (agent 1 :error-mode :fail)]
(send agt (fn [v] @p))
(send agt /)
(send agt inc)
(send agt inc)
(deliver p 0)
(Thread/sleep 100)
(is (= 0 @agt))
(is (= ArithmeticException (class (agent-error agt))))
(restart-agent agt 10)
(is (true? (await-for fragile-wait agt)))
(is (= 12 @agt))
(is (nil? (agent-error agt)))))
#_(deftest restart-clear
(let [p (promise)
agt (agent 1 :error-mode :fail)]
(send agt (fn [v] @p))
(send agt /)
(send agt inc)
(send agt inc)
(deliver p 0)
(Thread/sleep 100)
(is (= 0 @agt))
(is (= ArithmeticException (class (agent-error agt))))
(restart-agent agt 10 :clear-actions true)
(is (true? (await-for fragile-wait agt)))
(is (= 10 @agt))
(is (nil? (agent-error agt)))
(send agt inc)
(is (true? (await-for fragile-wait agt)))
(is (= 11 @agt))
(is (nil? (agent-error agt)))))
#_(deftest invalid-restart
(let [p (promise)
agt (agent 2 :error-mode :fail :validator even?)]
(is (thrown? RuntimeException (restart-agent agt 4)))
(send agt (fn [v] @p))
(send agt (partial + 2))
(send agt (partial + 2))
(deliver p 3)
(Thread/sleep 100)
(is (= 2 @agt))
(is (= IllegalStateException (class (agent-error agt))))
(is (thrown? RuntimeException (restart-agent agt 5)))
(restart-agent agt 6)
(is (true? (await-for fragile-wait agt)))
(is (= 10 @agt))
(is (nil? (agent-error agt)))))
(deftest earmuff-agent-bound
(let [a (agent 1)]
(send a (fn [_] *agent*))
(await a)
(is (= a @a))))
(def ^:dynamic *bind-me* :root-binding)
(deftest thread-conveyance-to-agents
(let [a (agent nil)]
(doto (Thread.
(fn []
(binding [*bind-me* :thread-binding]
(send a (constantly *bind-me*)))
(await a)))
(.start)
(.join))
(is (= @a :thread-binding))))
;; check for a race condition that was causing seque to leak threads from the
;; send-off pool. Specifically, if we consume all items from the seque, and
;; the LBQ continues to grow, it means there was an agent action blocking on
;; the .put, which would block indefinitely outside of this test.
(deftest seque-threads
(let [queue-size 5
slow-seq (for [x (take (* 2 queue-size) (iterate inc 0))]
(do (Thread/sleep 25)
x))
small-lbq (java.util.concurrent.LinkedBlockingQueue. queue-size)
worker (seque small-lbq slow-seq)]
(doall worker)
(is (= worker slow-seq))
(Thread/sleep 250) ;; make sure agents have time to run or get blocked
(let [queue-backlog (.size small-lbq)]
(is (<= 0 queue-backlog queue-size))
(when-not (zero? queue-backlog)
(.take small-lbq)
(Thread/sleep 250) ;; see if agent was blocking, indicating a thread leak
(is (= (.size small-lbq)
(dec queue-backlog)))))))
;; Check for a deadlock condition when one seque was fed into another
;; seque. Note that this test does not throw an exception or
;; otherwise fail if the issue is not fixed -- it simply deadlocks and
;; hangs until killed.
(deftest seque-into-seque-deadlock
(is (= (range 10) (seque 3 (seque 3 (range 10))))))
; http://clojure.org/agents
; agent
; deref, @-reader-macro, agent-errors
; send send-off clear-agent-errors
; await await-for
; set-validator get-validator
; add-watch remove-watch
; shutdown-agents
================================================
FILE: test/clojure/test_clojure/annotations/java_5.clj
================================================
;; java 5 annotation tests
(in-ns 'clojure.test-clojure.annotations)
(import [java.lang.annotation Annotation Retention RetentionPolicy Target ElementType])
(definterface Foo (foo []))
(deftype #^{Deprecated true
Retention RetentionPolicy/RUNTIME}
Bar [#^int a
#^{:tag int
Deprecated true
Retention RetentionPolicy/RUNTIME} b]
Foo (#^{Deprecated true
Retention RetentionPolicy/RUNTIME}
foo [this] 42))
(defn annotation->map
"Converts a Java annotation (which conceals data)
into a map (which makes is usable). Not lazy.
Works recursively. Returns non-annotations unscathed."
[#^java.lang.annotation.Annotation o]
(cond
(instance? Annotation o)
(let [type (.annotationType o)
itfs (-> (into #{type} (supers type)) (disj java.lang.annotation.Annotation))
data-methods (into #{} (mapcat #(.getDeclaredMethods %) itfs))]
(into
{:annotationType (.annotationType o)}
(map
(fn [m] [(keyword (.getName m)) (annotation->map (.invoke m o nil))])
data-methods)))
(or (sequential? o) (.isArray (class o)))
(map annotation->map o)
:else o))
(def expected-annotations
#{{:annotationType java.lang.annotation.Retention, :value RetentionPolicy/RUNTIME}
{:annotationType java.lang.Deprecated}})
(deftest test-annotations-on-type
(is (=
expected-annotations
(into #{} (map annotation->map (.getAnnotations Bar))))))
(deftest test-annotations-on-field
(is (=
expected-annotations
(into #{} (map annotation->map (.getAnnotations (.getField Bar "b")))))))
(deftest test-annotations-on-method
(is (=
expected-annotations
(into #{} (map annotation->map (.getAnnotations (.getMethod Bar "foo" nil)))))))
(gen-class :name foo.Bar
:extends clojure.lang.Box
:constructors {^{Deprecated true} [Object] [Object]}
:init init
:prefix "foo")
(defn foo-init [obj]
[[obj] nil])
(deftest test-annotations-on-constructor
(is (some #(instance? Deprecated %)
(for [ctor (.getConstructors (Class/forName "foo.Bar"))
annotation (.getAnnotations ctor)]
annotation))))
================================================
FILE: test/clojure/test_clojure/annotations/java_6.clj
================================================
;; java 6 annotation tests
(in-ns 'clojure.test-clojure.annotations)
(import [java.lang.annotation Annotation Retention RetentionPolicy Target ElementType]
[javax.xml.ws WebServiceRef WebServiceRefs])
(definterface Foo (foo []))
(deftype #^{Deprecated true
;Retention RetentionPolicy/RUNTIME
javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"]
javax.xml.ws.soap.Addressing {:enabled false :required true}
WebServiceRefs [(WebServiceRef {:name "fred" :type String})
(WebServiceRef {:name "ethel" :mappedName "lucy"})]}
Bar [#^int a
#^{:tag int
Deprecated true
;Retention RetentionPolicy/RUNTIME
;javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"]
;javax.xml.ws.soap.Addressing {:enabled false :required true}
;WebServiceRefs [(WebServiceRef {:name "fred" :type String})
; (WebServiceRef {:name "ethel" :mappedName "lucy"})]
}
b]
Foo (#^{Deprecated true
;Retention RetentionPolicy/RUNTIME
;javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"]
;javax.xml.ws.soap.Addressing {:enabled false :required true}
;WebServiceRefs [(WebServiceRef {:name "fred" :type String})
; (WebServiceRef {:name "ethel" :mappedName "lucy"})]
}
foo [this] 42))
(defn annotation->map
"Converts a Java annotation (which conceals data)
into a map (which makes is usable). Not lazy.
Works recursively. Returns non-annotations unscathed."
[#^java.lang.annotation.Annotation o]
(cond
(instance? Annotation o)
(let [type (.annotationType o)
itfs (-> (into #{type} (supers type)) (disj java.lang.annotation.Annotation))
data-methods (into #{} (mapcat #(.getDeclaredMethods %) itfs))]
(into
{:annotationType (.annotationType o)}
(map
(fn [m] [(keyword (.getName m)) (annotation->map (.invoke m o nil))])
data-methods)))
(or (sequential? o) (.isArray (class o)))
(map annotation->map o)
:else o))
(def expected-annotations
#{{:annotationType java.lang.annotation.Retention, :value RetentionPolicy/RUNTIME}
{:annotationType javax.xml.ws.WebServiceRefs,
:value [{:annotationType javax.xml.ws.WebServiceRef, :name "fred", :mappedName "", :type java.lang.String, :wsdlLocation "", :value java.lang.Object}
{:annotationType javax.xml.ws.WebServiceRef, :name "ethel", :mappedName "lucy", :type java.lang.Object, :wsdlLocation "", :value java.lang.Object}]}
{:annotationType javax.xml.ws.soap.Addressing, :enabled false, :required true}
{:annotationType javax.annotation.processing.SupportedOptions, :value ["foo" "bar" "baz"]}
{:annotationType java.lang.Deprecated}})
(deftest test-annotations-on-type
(is (=
expected-annotations
(into #{} (map annotation->map (.getAnnotations Bar))))))
(deftest test-annotations-on-field
(is (=
expected-annotations
(into #{} (map annotation->map (.getAnnotations (.getField Bar "b")))))))
(deftest test-annotations-on-method
(is (=
expected-annotations
(into #{} (map annotation->map (.getAnnotations (.getMethod Bar "foo" nil)))))))
(gen-class :name foo.Bar
:extends clojure.lang.Box
:constructors {^{Deprecated true} [Object] [Object]}
:init init
:prefix "foo")
(defn foo-init [obj]
[[obj] nil])
(deftest test-annotations-on-constructor
(is (some #(instance? Deprecated %)
(for [ctor (.getConstructors (Class/forName "foo.Bar"))
annotation (.getAnnotations ctor)]
annotation))))
================================================
FILE: test/clojure/test_clojure/annotations.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Authors: Stuart Halloway, Rich Hickey
(ns clojure.test-clojure.annotations
(:use clojure.test))
(case (System/getProperty "java.specification.version")
"1.6" (load "annotations/java_6")
nil)
================================================
FILE: test/clojure/test_clojure/api.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.api
(:require [clojure.test.generative :refer (defspec)]
[clojure.test-clojure.generators :as cgen])
(:import clojure.lang.IFn
clojure.java.api.Clojure
clojure.lang.Var))
(set! *warn-on-reflection* true)
(defn roundtrip
"Print an object and read it back with Clojure/read"
[o]
(binding [*print-length* nil
*print-dup* nil
*print-level* nil]
(Clojure/read (pr-str o))))
(defn api-var-str
[^Var v]
(Clojure/var (str (.name (.ns v)))
(str (.sym v))))
(defn api-var
[^Var v]
(Clojure/var (.name (.ns v))
(.sym v)))
(defspec api-can-read
roundtrip
[^{:tag cgen/ednable} o]
(when-not (= o %)
(throw (ex-info "Value cannot roundtrip with Clojure/read" {:printed o :read %}))))
(defspec api-can-find-var
api-var
[^{:tag cgen/var} v]
(when-not (= v %)
(throw (ex-info "Var cannot roundtrip through Clojure/var" {:from v :to %}))))
(defspec api-can-find-var-str
api-var-str
[^{:tag cgen/var} v]
(when-not (= v %)
(throw (ex-info "Var cannot roundtrip strings through Clojure/var" {:from v :to %}))))
================================================
FILE: test/clojure/test_clojure/atoms.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;Author: Frantisek Sodomka
(ns clojure.test-clojure.atoms
(:use clojure.test))
; http://clojure.org/atoms
; atom
; deref, @-reader-macro
; swap! reset!
; compare-and-set!
================================================
FILE: test/clojure/test_clojure/clojure_set.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Frantisek Sodomka
(ns clojure.test-clojure.clojure-set
(:use clojure.test)
(:require [clojure.set :as set]))
(deftest test-union
(are [x y] (= x y)
(set/union) #{}
; identity
(set/union #{}) #{}
(set/union #{1}) #{1}
(set/union #{1 2 3}) #{1 2 3}
; 2 sets, at least one is empty
(set/union #{} #{}) #{}
(set/union #{} #{1}) #{1}
(set/union #{} #{1 2 3}) #{1 2 3}
(set/union #{1} #{}) #{1}
(set/union #{1 2 3} #{}) #{1 2 3}
; 2 sets
(set/union #{1} #{2}) #{1 2}
(set/union #{1} #{1 2}) #{1 2}
(set/union #{2} #{1 2}) #{1 2}
(set/union #{1 2} #{3}) #{1 2 3}
(set/union #{1 2} #{2 3}) #{1 2 3}
; 3 sets, some are empty
(set/union #{} #{} #{}) #{}
(set/union #{1} #{} #{}) #{1}
(set/union #{} #{1} #{}) #{1}
(set/union #{} #{} #{1}) #{1}
(set/union #{1 2} #{2 3} #{}) #{1 2 3}
; 3 sets
(set/union #{1 2} #{3 4} #{5 6}) #{1 2 3 4 5 6}
(set/union #{1 2} #{2 3} #{1 3 4}) #{1 2 3 4}
; different data types
(set/union #{1 2} #{:a :b} #{nil} #{false true} #{\c "abc"} #{[] [1 2]}
#{{} {:a 1}} #{#{} #{1 2}})
#{1 2 :a :b nil false true \c "abc" [] [1 2] {} {:a 1} #{} #{1 2}}
; different types of sets
(set/union (hash-set) (hash-set 1 2) (hash-set 2 3))
(hash-set 1 2 3)
(set/union (sorted-set) (sorted-set 1 2) (sorted-set 2 3))
(sorted-set 1 2 3)
(set/union (hash-set) (hash-set 1 2) (hash-set 2 3)
(sorted-set) (sorted-set 4 5) (sorted-set 5 6))
(hash-set 1 2 3 4 5 6) ; also equals (sorted-set 1 2 3 4 5 6)
))
(deftest test-intersection
; at least one argument is needed
(is (thrown? IllegalArgumentException (set/intersection)))
(are [x y] (= x y)
; identity
(set/intersection #{}) #{}
(set/intersection #{1}) #{1}
(set/intersection #{1 2 3}) #{1 2 3}
; 2 sets, at least one is empty
(set/intersection #{} #{}) #{}
(set/intersection #{} #{1}) #{}
(set/intersection #{} #{1 2 3}) #{}
(set/intersection #{1} #{}) #{}
(set/intersection #{1 2 3} #{}) #{}
; 2 sets
(set/intersection #{1 2} #{1 2}) #{1 2}
(set/intersection #{1 2} #{3 4}) #{}
(set/intersection #{1 2} #{1}) #{1}
(set/intersection #{1 2} #{2}) #{2}
(set/intersection #{1 2 4} #{2 3 4 5}) #{2 4}
; 3 sets, some are empty
(set/intersection #{} #{} #{}) #{}
(set/intersection #{1} #{} #{}) #{}
(set/intersection #{1} #{1} #{}) #{}
(set/intersection #{1} #{} #{1}) #{}
(set/intersection #{1 2} #{2 3} #{}) #{}
; 3 sets
(set/intersection #{1 2} #{2 3} #{5 2}) #{2}
(set/intersection #{1 2 3} #{1 3 4} #{1 3}) #{1 3}
(set/intersection #{1 2 3} #{3 4 5} #{8 2 3}) #{3}
; different types of sets
(set/intersection (hash-set 1 2) (hash-set 2 3)) #{2}
(set/intersection (sorted-set 1 2) (sorted-set 2 3)) #{2}
(set/intersection
(hash-set 1 2) (hash-set 2 3)
(sorted-set 1 2) (sorted-set 2 3)) #{2} ))
(deftest test-difference
(are [x y] (= x y)
; identity
(set/difference #{}) #{}
(set/difference #{1}) #{1}
(set/difference #{1 2 3}) #{1 2 3}
; 2 sets
(set/difference #{1 2} #{1 2}) #{}
(set/difference #{1 2} #{3 4}) #{1 2}
(set/difference #{1 2} #{1}) #{2}
(set/difference #{1 2} #{2}) #{1}
(set/difference #{1 2 4} #{2 3 4 5}) #{1}
; 3 sets
(set/difference #{1 2} #{2 3} #{5 2}) #{1}
(set/difference #{1 2 3} #{1 3 4} #{1 3}) #{2}
(set/difference #{1 2 3} #{3 4 5} #{8 2 3}) #{1} ))
(deftest test-select
(are [x y] (= x y)
(set/select integer? #{}) #{}
(set/select integer? #{1 2}) #{1 2}
(set/select integer? #{1 2 :a :b :c}) #{1 2}
(set/select integer? #{:a :b :c}) #{}) )
(def compositions
#{{:name "Art of the Fugue" :composer "J. S. Bach"}
{:name "Musical Offering" :composer "J. S. Bach"}
{:name "Requiem" :composer "Giuseppe Verdi"}
{:name "Requiem" :composer "W. A. Mozart"}})
(deftest test-project
(are [x y] (= x y)
(set/project compositions [:name]) #{{:name "Art of the Fugue"}
{:name "Requiem"}
{:name "Musical Offering"}}
(set/project compositions [:composer]) #{{:composer "W. A. Mozart"}
{:composer "Giuseppe Verdi"}
{:composer "J. S. Bach"}}
(set/project compositions [:year]) #{{}}
(set/project #{{}} [:name]) #{{}} ))
(deftest test-rename
(are [x y] (= x y)
(set/rename compositions {:name :title}) #{{:title "Art of the Fugue" :composer "J. S. Bach"}
{:title "Musical Offering" :composer "J. S. Bach"}
{:title "Requiem" :composer "Giuseppe Verdi"}
{:title "Requiem" :composer "W. A. Mozart"}}
(set/rename compositions {:year :decade}) #{{:name "Art of the Fugue" :composer "J. S. Bach"}
{:name "Musical Offering" :composer "J. S. Bach"}
{:name "Requiem" :composer "Giuseppe Verdi"}
{:name "Requiem" :composer "W. A. Mozart"}}
(set/rename #{{}} {:year :decade}) #{{}}))
(deftest test-rename-keys
(are [x y] (= x y)
(set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"}
(set/rename-keys {:a "one" :b "two"} {:a :z :c :y}) {:z "one" :b "two"}
(set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"}))
(deftest test-index
(are [x y] (= x y)
(set/index #{{:c 2} {:b 1} {:a 1 :b 2}} [:b]) {{:b 2} #{{:a 1 :b 2}}, {:b 1} #{{:b 1}} {} #{{:c 2}}}
))
(deftest test-join
(are [x y] (= x y)
(set/join compositions compositions) compositions
(set/join compositions #{{:name "Art of the Fugue" :genre "Classical"}})
#{{:name "Art of the Fugue" :composer "J. S. Bach" :genre "Classical"}}
))
(deftest test-map-invert
(are [x y] (= x y)
(set/map-invert {:a "one" :b "two"}) {"one" :a "two" :b}))
(deftest test-subset?
(are [sub super] (set/subset? sub super)
#{} #{}
#{} #{1}
#{1} #{1}
#{1 2} #{1 2}
#{1 2} #{1 2 42}
#{false} #{false}
#{nil} #{nil}
#{nil} #{nil false}
#{1 2 nil} #{1 2 nil 4})
(are [notsub super] (not (set/subset? notsub super))
#{1} #{}
#{2} #{1}
#{1 3} #{1}
#{nil} #{false}
#{false} #{nil}
#{false nil} #{nil}
#{1 2 nil} #{1 2}))
(deftest test-superset?
(are [super sub] (set/superset? super sub)
#{} #{}
#{1} #{}
#{1} #{1}
#{1 2} #{1 2}
#{1 2 42} #{1 2}
#{false} #{false}
#{nil} #{nil}
#{false nil} #{false}
#{1 2 4 nil false} #{1 2 nil})
(are [notsuper sub] (not (set/superset? notsuper sub))
#{} #{1}
#{2} #{1}
#{1} #{1 3}
#{nil} #{false}
#{false} #{nil}
#{nil} #{false nil}
#{nil 2 3} #{false nil 2 3}))
================================================
FILE: test/clojure/test_clojure/clojure_walk.clj
================================================
(ns clojure.test-clojure.clojure-walk
(:require [clojure.walk :as w])
(:use clojure.test))
(deftest t-prewalk-replace
(is (= (w/prewalk-replace {:a :b} [:a {:a :a} (list 3 :c :a)])
[:b {:b :b} (list 3 :c :b)])))
(deftest t-postwalk-replace
(is (= (w/postwalk-replace {:a :b} [:a {:a :a} (list 3 :c :a)])
[:b {:b :b} (list 3 :c :b)])))
(deftest t-stringify-keys
(is (= (w/stringify-keys {:a 1, nil {:b 2 :c 3}, :d 4})
{"a" 1, nil {"b" 2 "c" 3}, "d" 4})))
(deftest t-prewalk-order
(is (= (let [a (atom [])]
(w/prewalk (fn [form] (swap! a conj form) form)
[1 2 {:a 3} (list 4 [5])])
@a)
[[1 2 {:a 3} (list 4 [5])]
1 2 {:a 3} [:a 3] :a 3 (list 4 [5])
4 [5] 5])))
(deftest t-postwalk-order
(is (= (let [a (atom [])]
(w/postwalk (fn [form] (swap! a conj form) form)
[1 2 {:a 3} (list 4 [5])])
@a)
[1 2
:a 3 [:a 3] {:a 3}
4 5 [5] (list 4 [5])
[1 2 {:a 3} (list 4 [5])]])))
(defrecord Foo [a b c])
(deftest walk
"Checks that walk returns the correct result and type of collection"
(let [colls ['(1 2 3)
[1 2 3]
#{1 2 3}
(sorted-set-by > 1 2 3)
{:a 1, :b 2, :c 3}
(sorted-map-by > 1 10, 2 20, 3 30)
(->Foo 1 2 3)
(map->Foo {:a 1 :b 2 :c 3 :extra 4})]]
(doseq [c colls]
(let [walked (w/walk identity identity c)]
(is (= c walked))
(is (= (type c) (type walked)))
(if (map? c)
(is (= (w/walk #(update-in % [1] inc) #(reduce + (vals %)) c)
(reduce + (map (comp inc val) c))))
(is (= (w/walk inc #(reduce + %) c)
(reduce + (map inc c)))))
(when (or (instance? clojure.lang.PersistentTreeMap c)
(instance? clojure.lang.PersistentTreeSet c))
(is (= (.comparator c) (.comparator walked))))))))
================================================
FILE: test/clojure/test_clojure/clojure_xml.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;Author: Frantisek Sodomka
(ns clojure.test-clojure.clojure-xml
(:use clojure.test)
(:require [clojure.xml :as xml]))
; parse
; emit-element
; emit
================================================
FILE: test/clojure/test_clojure/clojure_zip.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.clojure-zip
(:use clojure.test)
(:require [clojure.zip :as zip]))
; zipper
;
; seq-zip
; vector-zip
; xml-zip
;
; node
; branch?
; children
; make-node
; path
; lefts
; rights
; down
; up
; root
; right
; rightmost
; left
; leftmost
;
; insert-left
; insert-right
; replace
; edit
; insert-child
; append-child
; next
; prev
; end?
; remove
================================================
FILE: test/clojure/test_clojure/compilation/examples.clj
================================================
;; Copyright (c) Rich Hickey. All rights reserved.
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.compilation.examples)
(eval '(deftype X []))
(deftype T [])
================================================
FILE: test/clojure/test_clojure/compilation/line_number_examples.clj
================================================
(ns clojure.test-clojure.compilation.line-number-examples
"Example code taken from Paul Stadig and updated by Daniel Solano Gómez.
Original source at:
https://github.com/pjstadig/clojure-line-numbers/blob/master/src/clojure_line_numbers/core.clj"
(:import (clojure.lang PersistentHashMap)))
(defrecord Thing [field ^long primitive])
(defn instance-field
"I throw an exception in an instance field form."
[]
(.field
^Thing (identity nil)))
(defn instance-field-reflected
"I throw an exception in an instance field form."
[]
(.field
(identity nil)))
(defn instance-field-unboxed
"I throw an exception in an instance field form."
^long []
(.primitive
^Thing (identity nil)))
#_(defn instance-field-assign
"I throw an exception in an instance field assignment form."
[]
(set!
(.field
^Thing (identity nil))
(identity nil)))
#_(defn instance-field-assign-reflected
"I throw an exception in an instance field assignment form."
[]
(set!
(.field
(identity nil))
(identity nil)))
#_(defn static-field-assign
"I throw an exception in a static field assignment form."
[]
(set!
PersistentHashMap/EMPTY
(identity nil)))
(defn instance-method
"I throw an exception in an instance method form."
[]
(.without
^PersistentHashMap (identity nil)
:key))
(defn instance-method-reflected
"I throw an exception in an instance method form."
[]
(.without
(identity nil)
:key))
(defn instance-method-unboxed
"I throw an exception in an instance method form."
^long []
(.count
^PersistentHashMap (identity nil)))
(defn static-method
"I throw an exception in a static method form."
[]
(PersistentHashMap/create
^java.util.Map (identity nil)))
(defn static-method-reflected
"I throw an exception in a static method form."
[]
(String/copyValueOf
(identity nil)))
(defn static-method-unboxed
"I throw an exception in a static method form."
^long []
(Long/parseLong
^String (identity nil)))
(defn invoke
"I throw an exception in an invoke form."
[]
((identity nil)
(identity nil)))
(defn threading
"I throw an exception in a threading form."
[]
(-> :foo
(identity)
(identity)
((identity nil))
(identity)
(identity)))
(defn keyword-invoke
"I throw an exception in a keyword invoke."
[]
(letfn [(get-map []
(let [t (transient {})]
(persistent! t)
t))]
(:foo
(get-map))))
(defn invoke-cast
"I throw an exception casting to IFn in an invoke form."
[]
;; This code formatting is intentional.
(
(identity 1)
(identity nil)))
================================================
FILE: test/clojure/test_clojure/compilation.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.compilation
(:import (clojure.lang Compiler Compiler$CompilerException))
(:require [clojure.test.generative :refer (defspec)]
[clojure.data.generators :as gen]
[clojure.test-clojure.compilation.line-number-examples :as line])
(:use clojure.test
[clojure.test-helper :only (should-not-reflect should-print-err-message)]))
; http://clojure.org/compilation
; compile
; gen-class, gen-interface
(deftest test-compiler-metadata
(let [m (meta #'when)]
(are [x y] (= x y)
(list? (:arglists m)) true
(> (count (:arglists m)) 0) true
(string? (:doc m)) true
(> (.length (:doc m)) 0) true
(string? (:file m)) true
(> (.length (:file m)) 0) true
(integer? (:line m)) true
(> (:line m) 0) true
(integer? (:column m)) true
(> (:column m) 0) true
(:macro m) true
(:name m) 'when )))
(deftest test-embedded-constants
(testing "Embedded constants"
(is (eval `(= Boolean/TYPE ~Boolean/TYPE)))
(is (eval `(= Byte/TYPE ~Byte/TYPE)))
(is (eval `(= Character/TYPE ~Character/TYPE)))
(is (eval `(= Double/TYPE ~Double/TYPE)))
(is (eval `(= Float/TYPE ~Float/TYPE)))
(is (eval `(= Integer/TYPE ~Integer/TYPE)))
(is (eval `(= Long/TYPE ~Long/TYPE)))
(is (eval `(= Short/TYPE ~Short/TYPE)))))
(deftest test-compiler-resolution
(testing "resolve nonexistent class create should return nil (assembla #262)"
(is (nil? (resolve 'NonExistentClass.)))))
(deftest test-no-recur-across-try
(testing "don't recur to function from inside try"
(is (thrown? Compiler$CompilerException
(eval '(fn [x] (try (recur 1)))))))
(testing "don't recur to loop from inside try"
(is (thrown? Compiler$CompilerException
(eval '(loop [x 5]
(try (recur 1)))))))
(testing "don't recur to loop from inside of catch inside of try"
(is (thrown? Compiler$CompilerException
(eval '(loop [x 5]
(try
(catch Exception e
(recur 1))))))))
(testing "don't recur to loop from inside of finally inside of try"
(is (thrown? Compiler$CompilerException
(eval '(loop [x 5]
(try
(finally
(recur 1))))))))
(testing "don't get confused about what the recur is targeting"
(is (thrown? Compiler$CompilerException
(eval '(loop [x 5]
(try (fn [x]) (recur 1)))))))
(testing "don't allow recur across binding"
(is (thrown? Compiler$CompilerException
(eval '(fn [x] (binding [+ *] (recur 1)))))))
(testing "allow loop/recur inside try"
(is (= 0 (eval '(try (loop [x 3]
(if (zero? x) x (recur (dec x)))))))))
(testing "allow loop/recur fully inside catch"
(is (= 3 (eval '(try
(throw (Exception.))
(catch Exception e
(loop [x 0]
(if (< x 3) (recur (inc x)) x))))))))
(testing "allow loop/recur fully inside finally"
(is (= "012" (eval '(with-out-str
(try
:return-val-discarded-because-of-with-out-str
(finally (loop [x 0]
(when (< x 3)
(print x)
(recur (inc x)))))))))))
(testing "allow fn/recur inside try"
(is (= 0 (eval '(try
((fn [x]
(if (zero? x)
x
(recur (dec x))))
3)))))))
;; disabled until build box can call java from mvn
#_(deftest test-numeric-dispatch
(is (= "(int, int)" (TestDispatch/someMethod (int 1) (int 1))))
(is (= "(int, long)" (TestDispatch/someMethod (int 1) (long 1))))
(is (= "(long, long)" (TestDispatch/someMethod (long 1) (long 1)))))
(deftest test-CLJ-671-regression
(testing "that the presence of hints does not cause the compiler to infinitely loop"
(letfn [(gcd [x y]
(loop [x (long x) y (long y)]
(if (== y 0)
x
(recur y ^Long(rem x y)))))]
(is (= 4 (gcd 8 100))))))
;; ensure proper use of hints / type decls
(defn hinted
(^String [])
(^Integer [a])
(^java.util.List [a & args]))
;; fn names need to be fully-qualified because should-not-reflect evals its arg in a throwaway namespace
(deftest recognize-hinted-arg-vector
(should-not-reflect #(.substring (clojure.test-clojure.compilation/hinted) 0))
(should-not-reflect #(.floatValue (clojure.test-clojure.compilation/hinted "arg")))
(should-not-reflect #(.size (clojure.test-clojure.compilation/hinted :many :rest :args :here))))
(defn ^String hinting-conflict ^Integer [])
(deftest calls-use-arg-vector-hint
(should-not-reflect #(.floatValue (clojure.test-clojure.compilation/hinting-conflict)))
(should-print-err-message #"(?s)Reflection warning.*"
#(.substring (clojure.test-clojure.compilation/hinting-conflict) 0)))
(deftest deref-uses-var-tag
(should-not-reflect #(.substring clojure.test-clojure.compilation/hinting-conflict 0))
(should-print-err-message #"(?s)Reflection warning.*"
#(.floatValue clojure.test-clojure.compilation/hinting-conflict)))
(defn ^String legacy-hinting [])
(deftest legacy-call-hint
(should-not-reflect #(.substring (clojure.test-clojure.compilation/legacy-hinting) 0)))
(defprotocol HintedProtocol
(hintedp ^String [a]
^Integer [a b]))
(deftest hinted-protocol-arg-vector
(should-not-reflect #(.substring (clojure.test-clojure.compilation/hintedp "") 0))
(should-not-reflect #(.floatValue (clojure.test-clojure.compilation/hintedp :a :b))))
(defn primfn
(^long [])
(^double [a]))
(deftest primitive-return-decl
(should-not-reflect #(loop [k 5] (recur (clojure.test-clojure.compilation/primfn))))
(should-not-reflect #(loop [k 5.0] (recur (clojure.test-clojure.compilation/primfn 0))))
(should-print-err-message #"(?s).*k is not matching primitive.*"
#(loop [k (clojure.test-clojure.compilation/primfn)] (recur :foo))))
#_(deftest CLJ-1154-use-out-after-compile
;; This test creates a dummy file to compile, sets up a dummy
;; compiled output directory, and a dummy output stream, and
;; verifies the stream is still usable after compiling.
(spit "test/dummy.clj" "(ns dummy)")
(try
(let [compile-path (System/getProperty "clojure.compile.path")
tmp (java.io.File. "tmp")
new-out (java.io.OutputStreamWriter. (java.io.ByteArrayOutputStream.))]
(binding [clojure.core/*out* new-out]
(try
(.mkdir tmp)
(System/setProperty "clojure.compile.path" "tmp")
(clojure.lang.Compile/main (into-array ["dummy"]))
(println "this should still work without throwing an exception" )
(finally
(if compile-path
(System/setProperty "clojure.compile.path" compile-path)
(System/clearProperty "clojure.compile.path"))
(doseq [f (.listFiles tmp)]
(.delete f))
(.delete tmp)))))
(finally
(doseq [f (.listFiles (java.io.File. "test"))
:when (re-find #"dummy.clj" (str f))]
(.delete f)))))
(deftest CLJ-1184-do-in-non-list-test
(testing "do in a vector throws an exception"
(is (thrown? Compiler$CompilerException
(eval '[do 1 2 3]))))
(testing "do in a set throws an exception"
(is (thrown? Compiler$CompilerException
(eval '#{do}))))
;; compile uses a separate code path so we have to call it directly
;; to test it
(letfn [(compile [s]
(spit "test/clojure/bad_def_test.clj" (str "(ns clojure.bad-def-test)\n" s))
(try
(binding [*compile-path* "test"]
(clojure.core/compile 'clojure.bad-def-test))
(finally
(doseq [f (.listFiles (java.io.File. "test/clojure"))
:when (re-find #"bad_def_test" (str f))]
(.delete f)))))]
(testing "do in a vector throws an exception in compilation"
(is (thrown? Compiler$CompilerException (compile "[do 1 2 3]"))))
(testing "do in a set throws an exception in compilation"
(is (thrown? Compiler$CompilerException (compile "#{do}"))))))
(defn gen-name []
;; Not all names can be correctly demunged. Skip names that contain
;; a munge word as they will not properly demunge.
(let [munge-words (remove clojure.string/blank?
(conj (map #(clojure.string/replace % "_" "")
(vals Compiler/CHAR_MAP)) "_"))]
(first (filter (fn [n] (not-any? #(>= (.indexOf n %) 0) munge-words))
(repeatedly #(name (gen/symbol (constantly 10))))))))
(defn munge-roundtrip [n]
(Compiler/demunge (Compiler/munge n)))
(defspec test-munge-roundtrip
munge-roundtrip
[^{:tag clojure.test-clojure.compilation/gen-name} n]
(assert (= n %)))
(deftest test-fnexpr-type-hint
(testing "CLJ-1378: FnExpr should be allowed to override its reported class with a type hint."
(is (thrown? Compiler$CompilerException
(load-string "(.submit (java.util.concurrent.Executors/newCachedThreadPool) #())")))
(is (try (load-string "(.submit (java.util.concurrent.Executors/newCachedThreadPool) ^Runnable #())")
(catch Compiler$CompilerException e nil)))))
;(defrecord Y [a])
;#clojure.test_clojure.compilation.Y[1]
(defrecord Y [b])
#clojure.test_clojure.compilation.Y[1]
(binding [*compile-path* "target/test-classes"]
(compile 'clojure.test-clojure.compilation.examples))
(deftest test-compiler-line-numbers
(let [fails-on-line-number? (fn [expected function]
(try
(function)
nil
(catch Throwable t
(let [frames (filter #(= "line_number_examples.clj" (.getFileName %))
(.getStackTrace t))
_ (if (zero? (count frames))
(.printStackTrace t)
)
actual (.getLineNumber ^StackTraceElement (first frames))]
(= expected actual)))))]
(is (fails-on-line-number? 13 line/instance-field))
(is (fails-on-line-number? 19 line/instance-field-reflected))
(is (fails-on-line-number? 25 line/instance-field-unboxed))
;(is (fails-on-line-number? 32 line/instance-field-assign))
;(is (fails-on-line-number? 40 line/instance-field-assign-reflected))
;(is (fails-on-line-number? 47 line/static-field-assign))
(is (fails-on-line-number? 54 line/instance-method))
(is (fails-on-line-number? 61 line/instance-method-reflected))
(is (fails-on-line-number? 68 line/instance-method-unboxed))
(is (fails-on-line-number? 74 line/static-method))
(is (fails-on-line-number? 80 line/static-method-reflected))
(is (fails-on-line-number? 86 line/static-method-unboxed))
(is (fails-on-line-number? 92 line/invoke))
(is (fails-on-line-number? 101 line/threading))
(is (fails-on-line-number? 112 line/keyword-invoke))
(is (fails-on-line-number? 119 line/invoke-cast))))
(deftest CLJ-979
(is (= clojure.test_clojure.compilation.examples.X
(class (clojure.test-clojure.compilation.examples/->X))))
(is (.b (clojure.test_clojure.compilation.Y. 1)))
(is (= clojure.test_clojure.compilation.examples.T
(class (clojure.test_clojure.compilation.examples.T.))
(class (clojure.test-clojure.compilation.examples/->T)))))
(deftest clj-1568
(let [compiler-fails-at?
(fn [row col source]
(try
(Compiler/load (java.io.StringReader. source) (name (gensym "clj-1568.example-")) "clj-1568.example")
nil
(catch Compiler$CompilerException e
(re-find (re-pattern (str "^.*:" row ":" col "\\)$"))
(.getMessage e)))))]
(testing "with error in the initial form"
(are [row col source] (compiler-fails-at? row col source)
;; note that the spacing of the following string is important
1 4 " (.foo nil)"
2 18 "
(/ 1 0)"))
(testing "with error in an non-initial form"
(are [row col source] (compiler-fails-at? row col source)
;; note that the spacing of the following string is important
3 18 "(:foo {})
(.foo nil)"
4 20 "(ns clj-1568.example)
(/ 1 0)"))))
================================================
FILE: test/clojure/test_clojure/control.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka, Mike Hinchey, Stuart Halloway
;;
;; Test "flow control" constructs.
;;
(ns clojure.test-clojure.control
(:use clojure.test
clojure.test-helper))
;; *** Helper functions ***
(defn maintains-identity [f]
(are [x] (= (f x) x)
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2} ))
; http://clojure.org/special_forms
; http://clojure.org/macros
(deftest test-do
(are [x y] (= x y)
; no params => nil
(do) nil
; return last
(do 1) 1
(do 1 2) 2
(do 1 2 3 4 5) 5
; evaluate and return last
(let [a (atom 0)]
(do (reset! a (+ @a 1)) ; 1
(reset! a (+ @a 1)) ; 2
(reset! a (+ @a 1)) ; 3
@a)) 3 )
; identity (= (do x) x)
(maintains-identity (fn [_] (do _))) )
;; loop/recur
(deftest test-loop
(are [x y] (= x y)
1 (loop []
1)
3 (loop [a 1]
(if (< a 3)
(recur (inc a))
a))
[2 4 6] (loop [a []
b [1 2 3]]
(if (seq b)
(recur (conj a (* 2 (first b)))
(next b))
a))
[6 4 2] (loop [a ()
b [1 2 3]]
(if (seq b)
(recur (conj a (* 2 (first b)))
(next b))
a))
)
)
;; throw, try
; if: see logic.clj
(deftest test-when
(are [x y] (= x y)
1 (when true 1)
nil (when true)
nil (when false)
nil (when false (exception))
))
(deftest test-when-not
(are [x y] (= x y)
1 (when-not false 1)
nil (when-not true)
nil (when-not false)
nil (when-not true (exception))
))
(deftest test-if-not
(are [x y] (= x y)
1 (if-not false 1)
1 (if-not false 1 (exception))
nil (if-not true 1)
2 (if-not true 1 2)
nil (if-not true (exception))
1 (if-not true (exception) 1)
))
(deftest test-when-let
(are [x y] (= x y)
1 (when-let [a 1]
a)
2 (when-let [[a b] '(1 2)]
b)
nil (when-let [a false]
(exception))
))
(deftest test-if-let
(are [x y] (= x y)
1 (if-let [a 1]
a)
2 (if-let [[a b] '(1 2)]
b)
nil (if-let [a false]
(exception))
1 (if-let [a false]
a 1)
1 (if-let [[a b] nil]
b 1)
1 (if-let [a false]
(exception)
1)
))
(deftest test-when-first
(are [x y] (= x y)
1 (when-first [a [1 2]]
a)
2 (when-first [[a b] '((1 2) 3)]
b)
nil (when-first [a nil]
(exception))
))
(deftest test-if-some
(are [x y] (= x y)
1 (if-some [a 1] a)
false (if-some [a false] a)
nil (if-some [a nil] (exception))
3 (if-some [[a b] [1 2]] (+ a b))
1 (if-some [[a b] nil] b 1)
1 (if-some [a nil] (exception) 1)))
(deftest test-when-some
(are [x y] (= x y)
1 (when-some [a 1] a)
2 (when-some [[a b] [1 2]] b)
false (when-some [a false] a)
nil (when-some [a nil] (exception))))
(deftest test-cond
(are [x y] (= x y)
(cond) nil
(cond nil true) nil
(cond false true) nil
(cond true 1 true (exception)) 1
(cond nil 1 false 2 true 3 true 4) 3
(cond nil 1 false 2 true 3 true (exception)) 3 )
; false
(are [x] (= (cond x :a true :b) :b)
nil false )
; true
(are [x] (= (cond x :a true :b) :a)
true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2} )
; evaluation
(are [x y] (= x y)
(cond (> 3 2) (+ 1 2) true :result true (exception)) 3
(cond (< 3 2) (+ 1 2) true :result true (exception)) :result )
; identity (= (cond true x) x)
(maintains-identity (fn [_] (cond true _))) )
(deftest test-condp
(are [x] (= :pass x)
(condp = 1
1 :pass
2 :fail)
(condp = 1
2 :fail
1 :pass)
(condp = 1
2 :fail
:pass)
(condp = 1
:pass)
(condp = 1
2 :fail
;; doc of condp says result-expr is returned
;; shouldn't it say similar to cond: "evaluates and returns
;; the value of the corresponding expr and doesn't evaluate any of the
;; other tests or exprs."
(identity :pass))
(condp + 1
1 :>> #(if (= % 2) :pass :fail))
(condp + 1
1 :>> #(if (= % 3) :fail :pass))
)
(is (thrown? IllegalArgumentException
(condp = 1)
))
(is (thrown? IllegalArgumentException
(condp = 1
2 :fail)
))
)
; [for, doseq (for.clj)]
(deftest test-dotimes
;; dotimes always returns nil
(is (= nil (dotimes [n 1] n)))
;; test using an atom since dotimes is for modifying
;; test executes n times
(is (= 3
(let [a (atom 0)]
(dotimes [n 3]
(swap! a inc))
@a)
))
;; test all values of n
(is (= [0 1 2]
(let [a (atom [])]
(dotimes [n 3]
(swap! a conj n))
@a)))
(is (= []
(let [a (atom [])]
(dotimes [n 0]
(swap! a conj n))
@a)))
)
(deftest test-while
(is (= nil (while nil (throw (Exception. "never")))))
(is (= [0 nil]
;; a will dec to 0
;; while always returns nil
(let [a (atom 3)
w (while (pos? @a)
(swap! a dec))]
[@a w])))
(is (thrown? Exception (while true (throw (Exception. "expected to throw")))))
)
; locking, monitor-enter, monitor-exit
; case
(comment
(deftest test-case
(testing "can match many kinds of things"
(let [two 2
test-fn
#(case %
1 :number
"foo" :string
\a :char
pow :symbol
:zap :keyword
(2 \b "bar") :one-of-many
[1 2] :sequential-thing
{:a 2} :map
{:r 2 :d 2} :droid
#{2 3 4 5} :set
[1 [[[2]]]] :deeply-nested
nil :nil
:default)]
(are [result input] (= result (test-fn input))
:number 1
:string "foo"
:char \a
:keyword :zap
:symbol 'pow
:one-of-many 2
:one-of-many \b
:one-of-many "bar"
:sequential-thing [1 2]
:sequential-thing (list 1 2)
:sequential-thing [1 two]
:map {:a 2}
:map {:a two}
:set #{2 3 4 5}
:set #{two 3 4 5}
:default #{2 3 4 5 6}
:droid {:r 2 :d 2}
:deeply-nested [1 [[[two]]]]
:nil nil
:default :anything-not-appearing-above)))
(testing "throws IllegalArgumentException if no match"
(is (thrown-with-msg?
IllegalArgumentException #"No matching clause: 2"
(case 2 1 :ok))))
(testing "sorting doesn't matter"
(let [test-fn
#(case %
{:b 2 :a 1} :map
#{3 2 1} :set
:default)]
(are [result input] (= result (test-fn input))
:map {:a 1 :b 2}
:map (sorted-map :a 1 :b 2)
:set #{3 2 1}
:set (sorted-set 2 1 3))))
(testing "test number equivalence"
(is (= :1 (case 1N 1 :1 :else))))
(comment
(testing "test warn when boxing/hashing expr for all-ints case"
(should-print-err-message
#"Performance warning, .*:\d+ - case has int tests, but tested expression is not primitive..*\r?\n"
(let [x (Object.)] (case x 1 1 2)))))
(testing "test correct behavior on sparse ints"
(are [result input] (= result (case input
2r1000000000000000000000000000000 :big
1 :small
:else))
:small 1
:big 1073741824
:else 2)
(are [result input] (= result (case input
1 :small
2r1000000000000000000000000000000 :big
:else))
:small 1
:big 1073741824
:else 2))
(testing "test emits return types"
(should-not-reflect (Long. (case 1 1 1))) ; new Long(long)
(should-not-reflect (Long. (case 1 1 "1")))) ; new Long(String)
(testing "non-equivalence of chars and nums"
(are [result input] (= result (case input 97 :97 :else))
:else \a
:else (char \a)
:97 (int \a))
(are [result input] (= result (case input \a :a :else))
:else 97
:else 97N
:a (char 97)))
(testing "test error on duplicate test constants"
(is (thrown-with-msg?
IllegalArgumentException
#"Duplicate case test constant: 1"
(eval `(case 0 1 :x 1 :y)))))
(testing "test correct behaviour on Number truncation"
(let [^Object x (Long. 8589934591) ; force bindings to not be emitted as a primitive long
^Object y (Long. -1)]
(is (= :diff (case x -1 :oops :diff)))
(is (= :same (case y -1 :same :oops)))))
(testing "test correct behavior on hash collision"
;; case uses Java .hashCode to put values into hash buckets.
(is (== (.hashCode 1) (.hashCode 9223372039002259457N)))
(are [result input] (= result (case input
1 :long
9223372039002259457N :big
:else))
:long 1
:big 9223372039002259457N
:else 4294967296
:else 2)
(are [result input] (= result (case input
9223372039002259457N :big
1 :long
:else))
:long 1
:big 9223372039002259457N
:else 4294967296
:else 2)
(are [result input] (= result (case input
0 :zero
-1 :neg1
2 :two
:oops :OOPS))
:zero 0
:neg1 -1
:two 2
:OOPS :oops)
(are [result input] (= result (case input
1204766517646190306 :a
1 :b
-2 :c
:d))
:a 1204766517646190306
:b 1
:c -2
:d 4294967296
:d 3))
(comment
(testing "test warn for hash collision"
(should-print-err-message
#"Performance warning, .*:\d+ - hash collision of some case test constants; if selected, those entries will be tested sequentially..*\r?\n"
(case 1 1 :long 9223372039002259457N :big 2))))
(testing "test constants are *not* evaluated"
(let [test-fn
;; never write code like this...
#(case %
(throw (RuntimeException. "boom")) :piece-of-throw-expr
:no-match)]
(are [result input] (= result (test-fn input))
:piece-of-throw-expr 'throw
:piece-of-throw-expr '[RuntimeException. "boom"]
:no-match nil))))
)
================================================
FILE: test/clojure/test_clojure/data.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.data
(:use clojure.data clojure.test)
(import java.util.HashSet))
(deftest diff-test
(are [d x y] (= d (diff x y))
[nil nil nil] nil nil
[1 2 nil] 1 2
[nil nil [1 2 3]] [1 2 3] '(1 2 3)
[1 [:a :b] nil] 1 [:a :b]
[{:a 1} :b nil] {:a 1} :b
[:team #{:p1 :p2} nil] :team #{:p1 :p2}
[{0 :a} [:a] nil] {0 :a} [:a]
[nil [nil 2] [1]] [1] [1 2]
[nil nil [1 2]] [1 2] (into-array [1 2])
[#{:a} #{:b} #{:c :d}] #{:a :c :d} #{:b :c :d}
[nil nil {:a 1}] {:a 1} {:a 1}
[{:a #{2}} {:a #{4}} {:a #{3}}] {:a #{2 3}} {:a #{3 4}}
[#{1} #{3} #{2}] (HashSet. [1 2]) (HashSet. [2 3])
[nil nil [1 2]] [1 2] (into-array [1 2])
[nil nil [1 2]] (into-array [1 2]) [1 2]
[{:a {:c [1]}} {:a {:c [0]}} {:a {:c [nil 2] :b 1}}] {:a {:b 1 :c [1 2]}} {:a {:b 1 :c [0 2]}}
[{:a nil} {:a false} {:b nil :c false}] {:a nil :b nil :c false} {:a false :b nil :c false}))
================================================
FILE: test/clojure/test_clojure/data_structures.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.data-structures
(:use clojure.test
[clojure.test.generative :exclude (is)])
(:require [clojure.test-clojure.generators :as cgen]
[clojure.data.generators :as gen]
[clojure.string :as string]))
;; *** Helper functions ***
(defn diff [s1 s2]
(seq (reduce disj (set s1) (set s2))))
;; *** Generative ***
(defspec subcollection-counts-are-consistent
identity
[^{:tag cgen/ednable-collection} coll]
(let [n (count coll)]
(dotimes [i n]
(is (= n
(+ i (count (nthnext coll i)))
(+ i (count (drop i coll))))))))
(defn- transient? [x]
(instance? clojure.lang.ITransientCollection x))
(defn gen-transient-set-action []
(gen/rand-nth [[#(conj! %1 %2) #(conj %1 %2) (gen/uniform -100 100)]
[#(disj! %1 %2) #(disj %1 %2) (gen/uniform -100 100)]
[#(deref (future (conj! %1 %2))) #(conj %1 %2) (gen/uniform -100 100)]
[#(deref (future (disj! %1 %2))) #(disj %1 %2) (gen/uniform -100 100)]
[persistent! identity]
[identity transient]]))
(defn gen-transient-set-actions []
(gen/reps #(gen/uniform 0 100) gen-transient-set-action))
(defn tempty? [t]
(= (count t) 0))
(defn gen-transient-vector-action []
(gen/rand-nth [[#(conj! %1 %2) #(conj %1 %2) (gen/uniform -100 100)]
[(fn [v _] (if (tempty? v) v (pop! v)))
(fn [v _] (if (tempty? v) v (pop v)))
(gen/uniform -100 100)]
[#(deref (future (conj! %1 %2))) #(conj %1 %2) (gen/uniform -100 100)]
[(fn [v _] (if (tempty? v) v (deref (future (pop! v)))))
(fn [v _] (if (tempty? v) v (pop v)))
(gen/uniform -100 100)]
[persistent! identity]
[identity transient]]))
(defn gen-transient-vector-actions []
(gen/reps #(gen/uniform 0 100) gen-transient-vector-action))
(defn gen-transient-map-action []
(gen/rand-nth [[#(assoc! %1 %2 %2) #(assoc %1 %2 %2) (gen/uniform -100 100)]
[#(dissoc! %1 %2) #(dissoc %1 %2) (gen/uniform -100 100)]
[#(deref (future (assoc! %1 %2 %2))) #(assoc %1 %2 %2) (gen/uniform -100 100)]
[#(deref (future (dissoc! %1 %2))) #(dissoc %1 %2) (gen/uniform -100 100)]
[persistent! identity]
[identity transient]]))
(defn gen-transient-map-actions []
(gen/reps #(gen/uniform 0 100) gen-transient-map-action))
(defn assert-same-collection [a b]
(assert (= (count a) (count b) (.size a) (.size b)))
(assert (= a b))
(assert (= b a))
(assert (.equals ^Object a b))
(assert (.equals ^Object b a))
(assert (= (hash a) (hash b)))
(assert (= (.hashCode ^Object a) (.hashCode ^Object b)))
(assert (= a
(into (empty a) a)
(into (empty b) b)
(into (empty a) b)
(into (empty b) a))))
(defn apply-actions [coll actions]
(reduce (fn [c [tfunc pfunc & args]]
(apply (if (transient? c) tfunc pfunc) c args))
coll
actions))
(defn to-persistent [c]
(if (transient? c) (persistent! c) c))
(defspec same-output-persistent-transient-set
identity
[^{:tag clojure.test-clojure.data-structures/gen-transient-set-actions} actions]
(assert-same-collection
(to-persistent (apply-actions #{} actions))
(to-persistent (apply-actions #{} actions))))
(defspec same-output-persistent-transient-vector
identity
[^{:tag clojure.test-clojure.data-structures/gen-transient-vector-actions} actions]
(assert-same-collection
(to-persistent (apply-actions [] actions))
(to-persistent (apply-actions [] actions))))
(defspec same-output-persistent-transient-map
identity
[^{:tag clojure.test-clojure.data-structures/gen-transient-map-actions} actions]
(assert-same-collection
(to-persistent (apply-actions clojure.lang.PersistentArrayMap/EMPTY actions))
(to-persistent (apply-actions clojure.lang.PersistentArrayMap/EMPTY actions)))
(assert-same-collection
(to-persistent (apply-actions clojure.lang.PersistentHashMap/EMPTY actions))
(to-persistent (apply-actions clojure.lang.PersistentHashMap/EMPTY actions))))
;; *** General ***
(defstruct equality-struct :a :b)
(deftest test-equality
; nil is not equal to any other value
(are [x] (not (= nil x))
true false
0 0.0
\space
"" #""
() [] #{} {}
(lazy-seq nil) ; SVN 1292: fixed (= (lazy-seq nil) nil)
(lazy-seq ())
(lazy-seq [])
(lazy-seq {})
(lazy-seq #{})
(lazy-seq "")
(lazy-seq (into-array []))
(new Object) )
; numbers equality across types (see tests below - NOT IMPLEMENTED YET)
; ratios
(is (== 1/2 0.5))
(is (== 1/1000 0.001))
(is (not= 2/3 0.6666666666666666))
; vectors equal other seqs by items equality
(are [x y] (= x y)
'() [] ; regression fixed in r1208; was not equal
'(1) [1]
'(1 2) [1 2]
[] '() ; same again, but vectors first
[1] '(1)
[1 2] '(1 2) )
(is (not= [1 2] '(2 1))) ; order of items matters
; list and vector vs. set and map
(are [x y] (not= x y)
; only () equals []
() #{}
() {}
[] #{}
[] {}
#{} {}
; only '(1) equals [1]
'(1) #{1}
[1] #{1} )
; sorted-map, hash-map and array-map - classes differ, but content is equal
;; TODO: reimplement all-are with new do-template?
;; (all-are (not= (class _1) (class _2))
;; (sorted-map :a 1)
;; (hash-map :a 1)
;; (array-map :a 1))
;; (all-are (= _1 _2)
;; (sorted-map)
;; (hash-map)
;; (array-map))
;; (all-are (= _1 _2)
;; (sorted-map :a 1)
;; (hash-map :a 1)
;; (array-map :a 1))
;; (all-are (= _1 _2)
;; (sorted-map :a 1 :z 3 :c 2)
;; (hash-map :a 1 :z 3 :c 2)
;; (array-map :a 1 :z 3 :c 2))
; struct-map vs. sorted-map, hash-map and array-map
(are [x] (and (not= (class (struct equality-struct 1 2)) (class x))
(= (struct equality-struct 1 2) x))
(sorted-map-by compare :a 1 :b 2)
(sorted-map :a 1 :b 2)
(hash-map :a 1 :b 2)
(array-map :a 1 :b 2))
; sorted-set vs. hash-set
(is (not= (class (sorted-set 1)) (class (hash-set 1))))
(are [x y] (= x y)
(sorted-set-by <) (hash-set)
(sorted-set-by < 1) (hash-set 1)
(sorted-set-by < 3 2 1) (hash-set 3 2 1)
(sorted-set) (hash-set)
(sorted-set 1) (hash-set 1)
(sorted-set 3 2 1) (hash-set 3 2 1) ))
;; *** Collections ***
(deftest test-count
(let [EMPTY clojure.lang.PersistentQueue/EMPTY]
(are [x y] (= (count x) y)
EMPTY 0
(into EMPTY [:a :b]) 2
(-> (into EMPTY [:a :b]) pop pop) 0
nil 0
() 0
'(1) 1
'(1 2 3) 3
[] 0
[1] 1
[1 2 3] 3
#{} 0
#{1} 1
#{1 2 3} 3
{} 0
{:a 1} 1
{:a 1 :b 2 :c 3} 3
"" 0
"a" 1
"abc" 3
(into-array []) 0
(into-array [1]) 1
(into-array [1 2 3]) 3
(java.util.ArrayList. ^clojure.lang.IPersistentVector []) 0
(java.util.ArrayList. ^clojure.lang.IPersistentVector [1]) 1
(java.util.ArrayList. ^clojure.lang.IPersistentVector [1 2 3]) 3
(java.util.HashMap. ^clojure.lang.IPersistentMap {}) 0
(java.util.HashMap. ^clojure.lang.IPersistentMap {:a 1}) 1
(java.util.HashMap. ^clojure.lang.IPersistentMap {:a 1 :b 2 :c 3}) 3 ))
; different types
(are [x] (= (count [x]) 1)
nil true false
0 0.0 "" \space
() [] #{} {} ))
(deftest test-conj
; doesn't work on strings or arrays
(is (thrown? ClassCastException (conj "" \a)))
(is (thrown? ClassCastException (conj (into-array []) 1)))
(are [x y] (= x y)
(conj nil 1) '(1)
(conj nil 3 2 1) '(1 2 3)
(conj nil nil) '(nil)
(conj nil nil nil) '(nil nil)
(conj nil nil nil 1) '(1 nil nil)
; list -> conj puts the item at the front of the list
(conj () 1) '(1)
(conj () 1 2) '(2 1)
(conj '(2 3) 1) '(1 2 3)
(conj '(2 3) 1 4 3) '(3 4 1 2 3)
(conj () nil) '(nil)
(conj () ()) '(())
; vector -> conj puts the item at the end of the vector
(conj [] 1) [1]
(conj [] 1 2) [1 2]
(conj [2 3] 1) [2 3 1]
(conj [2 3] 1 4 3) [2 3 1 4 3]
(conj [] nil) [nil]
(conj [] []) [[]]
; map -> conj expects another (possibly single entry) map as the item,
; and returns a new map which is the old map plus the entries
; from the new, which may overwrite entries of the old.
; conj also accepts a MapEntry or a vector of two items (key and value).
(conj {} {}) {}
(conj {} {:a 1}) {:a 1}
(conj {} {:a 1 :b 2}) {:a 1 :b 2}
(conj {} {:a 1 :b 2} {:c 3}) {:a 1 :b 2 :c 3}
(conj {} {:a 1 :b 2} {:a 3 :c 4}) {:a 3 :b 2 :c 4}
(conj {:a 1} {:a 7}) {:a 7}
(conj {:a 1} {:b 2}) {:a 1 :b 2}
(conj {:a 1} {:a 7 :b 2}) {:a 7 :b 2}
(conj {:a 1} {:a 7 :b 2} {:c 3}) {:a 7 :b 2 :c 3}
(conj {:a 1} {:a 7 :b 2} {:b 4 :c 5}) {:a 7 :b 4 :c 5}
(conj {} (first {:a 1})) {:a 1} ; MapEntry
(conj {:a 1} (first {:b 2})) {:a 1 :b 2}
(conj {:a 1} (first {:a 7})) {:a 7}
(conj {:a 1} (first {:b 2}) (first {:a 5})) {:a 5 :b 2}
(conj {} [:a 1]) {:a 1} ; vector
(conj {:a 1} [:b 2]) {:a 1 :b 2}
(conj {:a 1} [:a 7]) {:a 7}
(conj {:a 1} [:b 2] [:a 5]) {:a 5 :b 2}
(conj {} {nil {}}) {nil {}}
(conj {} {{} nil}) {{} nil}
(conj {} {{} {}}) {{} {}}
; set
(conj #{} 1) #{1}
(conj #{} 1 2 3) #{1 2 3}
(conj #{2 3} 1) #{3 1 2}
(conj #{3 2} 1) #{1 2 3}
(conj #{2 3} 2) #{2 3}
(conj #{2 3} 2 3) #{2 3}
(conj #{2 3} 4 1 2 3) #{1 2 3 4}
(conj #{} nil) #{nil}
(conj #{} #{}) #{#{}} ))
;; *** Lists and Vectors ***
(deftest test-peek
; doesn't work for sets and maps
(is (thrown? ClassCastException (peek #{1})))
(is (thrown? ClassCastException (peek {:a 1})))
(are [x y] (= x y)
(peek nil) nil
; list = first
(peek ()) nil
(peek '(1)) 1
(peek '(1 2 3)) 1
(peek '(nil)) nil ; special cases
(peek '(1 nil)) 1
(peek '(nil 2)) nil
(peek '(())) ()
(peek '(() nil)) ()
(peek '(() 2 nil)) ()
; vector = last
(peek []) nil
(peek [1]) 1
(peek [1 2 3]) 3
(peek [nil]) nil ; special cases
(peek [1 nil]) nil
(peek [nil 2]) 2
(peek [[]]) []
(peek [[] nil]) nil
(peek [[] 2 nil]) nil ))
(deftest test-pop
; doesn't work for sets and maps
(is (thrown? ClassCastException (pop #{1})))
(is (thrown? ClassCastException (pop #{:a 1})))
; collection cannot be empty
(is (thrown? IllegalStateException (pop ())))
(is (thrown? IllegalStateException (pop [])))
(are [x y] (= x y)
(pop nil) nil
; list - pop first
(pop '(1)) ()
(pop '(1 2 3)) '(2 3)
(pop '(nil)) ()
(pop '(1 nil)) '(nil)
(pop '(nil 2)) '(2)
(pop '(())) ()
(pop '(() nil)) '(nil)
(pop '(() 2 nil)) '(2 nil)
; vector - pop last
(pop [1]) []
(pop [1 2 3]) [1 2]
(pop [nil]) []
(pop [1 nil]) [1]
(pop [nil 2]) [nil]
(pop [[]]) []
(pop [[] nil]) [[]]
(pop [[] 2 nil]) [[] 2] ))
;; *** Lists (IPersistentList) ***
(deftest test-list
(are [x] (list? x)
()
'()
(list)
(list 1 2 3) )
; order is important
(are [x y] (not (= x y))
(list 1 2) (list 2 1)
(list 3 1 2) (list 1 2 3) )
(are [x y] (= x y)
'() ()
(list) '()
(list 1) '(1)
(list 1 2) '(1 2)
; nesting
(list 1 (list 2 3) (list 3 (list 4 5 (list 6 (list 7)))))
'(1 (2 3) (3 (4 5 (6 (7)))))
; different data structures
(list true false nil)
'(true false nil)
(list 1 2.5 2/3 "ab" \x 'cd :kw)
'(1 2.5 2/3 "ab" \x cd :kw)
(list (list 1 2) [3 4] {:a 1 :b 2} #{:c :d})
'((1 2) [3 4] {:a 1 :b 2} #{:c :d})
; evaluation
(list (+ 1 2) [(+ 2 3) 'a] (list (* 2 3) 8))
'(3 [5 a] (6 8))
; special cases
(list nil) '(nil)
(list 1 nil) '(1 nil)
(list nil 2) '(nil 2)
(list ()) '(())
(list 1 ()) '(1 ())
(list () 2) '(() 2) ))
;; *** Maps (IPersistentMap) ***
(deftest test-find
(are [x y] (= x y)
(find {} :a) nil
(find {:a 1} :a) [:a 1]
(find {:a 1} :b) nil
(find {nil 1} nil) [nil 1]
(find {:a 1 :b 2} :a) [:a 1]
(find {:a 1 :b 2} :b) [:b 2]
(find {:a 1 :b 2} :c) nil
(find {} nil) nil
(find {:a 1} nil) nil
(find {:a 1 :b 2} nil) nil ))
(deftest test-contains?
; contains? is designed to work preferably on maps and sets
(are [x y] (= x y)
(contains? {} :a) false
(contains? {} nil) false
(contains? {:a 1} :a) true
(contains? {:a 1} :b) false
(contains? {:a 1} nil) false
(contains? {nil 1} nil) true
(contains? {:a 1 :b 2} :a) true
(contains? {:a 1 :b 2} :b) true
(contains? {:a 1 :b 2} :c) false
(contains? {:a 1 :b 2} nil) false
; sets
(contains? #{} 1) false
(contains? #{} nil) false
(contains? #{1} 1) true
(contains? #{1} 2) false
(contains? #{1} nil) false
(contains? #{1 2 3} 1) true
(contains? #{1 2 3} 3) true
(contains? #{1 2 3} 10) false
(contains? #{1 2 3} nil) false)
; contains? also works on java.util.Map and java.util.Set.
(are [x y] (= x y)
(contains? (java.util.HashMap. {}) :a) false
(contains? (java.util.HashMap. {}) nil) false
(contains? (java.util.HashMap. {:a 1}) :a) true
(contains? (java.util.HashMap. {:a 1}) :b) false
(contains? (java.util.HashMap. {:a 1}) nil) false
(contains? (java.util.HashMap. {:a 1 :b 2}) :a) true
(contains? (java.util.HashMap. {:a 1 :b 2}) :b) true
(contains? (java.util.HashMap. {:a 1 :b 2}) :c) false
(contains? (java.util.HashMap. {:a 1 :b 2}) nil) false
; sets
(contains? (java.util.HashSet. #{}) 1) false
(contains? (java.util.HashSet. #{}) nil) false
(contains? (java.util.HashSet. #{1}) 1) true
(contains? (java.util.HashSet. #{1}) 2) false
(contains? (java.util.HashSet. #{1}) nil) false
(contains? (java.util.HashSet. #{1 2 3}) 1) true
(contains? (java.util.HashSet. #{1 2 3}) 3) true
(contains? (java.util.HashSet. #{1 2 3}) 10) false
(contains? (java.util.HashSet. #{1 2 3}) nil) false)
; numerically indexed collections (e.g. vectors and Java arrays)
; => test if the numeric key is WITHIN THE RANGE OF INDEXES
(are [x y] (= x y)
(contains? [] 0) false
(contains? [] -1) false
(contains? [] 1) false
(contains? [1] 0) true
(contains? [1] -1) false
(contains? [1] 1) false
(contains? [1 2 3] 0) true
(contains? [1 2 3] 2) true
(contains? [1 2 3] 3) false
(contains? [1 2 3] -1) false
; arrays
(contains? (into-array []) 0) false
(contains? (into-array []) -1) false
(contains? (into-array []) 1) false
(contains? (into-array [1]) 0) true
(contains? (into-array [1]) -1) false
(contains? (into-array [1]) 1) false
(contains? (into-array [1 2 3]) 0) true
(contains? (into-array [1 2 3]) 2) true
(contains? (into-array [1 2 3]) 3) false
(contains? (into-array [1 2 3]) -1) false)
; 'contains?' will not operate on non-associative things
(are [x] (is (thrown? Exception (contains? x 1)))
'(1 2 3)
3))
(deftest test-keys
(are [x y] (= x y) ; other than map data structures
(keys ()) nil
(keys []) nil
(keys #{}) nil
(keys "") nil )
(are [x y] (= x y)
; (class {:a 1}) => clojure.lang.PersistentArrayMap
(keys {}) nil
(keys {:a 1}) '(:a)
(keys {nil 1}) '(nil)
(diff (keys {:a 1 :b 2}) '(:a :b)) nil ; (keys {:a 1 :b 2}) '(:a :b)
; (class (sorted-map :a 1)) => clojure.lang.PersistentTreeMap
(keys (sorted-map)) nil
(keys (sorted-map :a 1)) '(:a)
(diff (keys (sorted-map :a 1 :b 2)) '(:a :b)) nil ; (keys (sorted-map :a 1 :b 2)) '(:a :b)
; (class (hash-map :a 1)) => clojure.lang.PersistentHashMap
(keys (hash-map)) nil
(keys (hash-map :a 1)) '(:a)
(diff (keys (hash-map :a 1 :b 2)) '(:a :b)) nil ) ; (keys (hash-map :a 1 :b 2)) '(:a :b)
(let [m {:a 1 :b 2}
k (keys m)]
(is (= {:hi :there} (meta (with-meta k {:hi :there}))))))
(deftest test-vals
(are [x y] (= x y) ; other than map data structures
(vals ()) nil
(vals []) nil
(vals #{}) nil
(vals "") nil )
(are [x y] (= x y)
; (class {:a 1}) => clojure.lang.PersistentArrayMap
(vals {}) nil
(vals {:a 1}) '(1)
(vals {nil 1}) '(1)
(diff (vals {:a 1 :b 2}) '(1 2)) nil ; (vals {:a 1 :b 2}) '(1 2)
; (class (sorted-map :a 1)) => clojure.lang.PersistentTreeMap
(vals (sorted-map)) nil
(vals (sorted-map :a 1)) '(1)
(diff (vals (sorted-map :a 1 :b 2)) '(1 2)) nil ; (vals (sorted-map :a 1 :b 2)) '(1 2)
; (class (hash-map :a 1)) => clojure.lang.PersistentHashMap
(vals (hash-map)) nil
(vals (hash-map :a 1)) '(1)
(diff (vals (hash-map :a 1 :b 2)) '(1 2)) nil ) ; (vals (hash-map :a 1 :b 2)) '(1 2)
(let [m {:a 1 :b 2}
v (vals m)]
(is (= {:hi :there} (meta (with-meta v {:hi :there}))))))
(deftest test-key
(are [x] (= (key (first (hash-map x :value))) x)
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2} ))
(deftest test-val
(are [x] (= (val (first (hash-map :key x))) x)
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2} ))
(deftest test-get
(let [m {:a 1, :b 2, :c {:d 3, :e 4}, :f nil, :g false, nil {:h 5}}]
(is (thrown? IllegalArgumentException (get-in {:a 1} 5)))
(are [x y] (= x y)
(get m :a) 1
(get m :e) nil
(get m :e 0) 0
(get m nil) {:h 5}
(get m :b 0) 2
(get m :f 0) nil
(get-in m [:c :e]) 4
(get-in m '(:c :e)) 4
(get-in m [:c :x]) nil
(get-in m [:f]) nil
(get-in m [:g]) false
(get-in m [:h]) nil
(get-in m []) m
(get-in m nil) m
(get-in m [:c :e] 0) 4
(get-in m '(:c :e) 0) 4
(get-in m [:c :x] 0) 0
(get-in m [:b] 0) 2
(get-in m [:f] 0) nil
(get-in m [:g] 0) false
(get-in m [:h] 0) 0
(get-in m [:x :y] {:y 1}) {:y 1}
(get-in m [] 0) m
(get-in m nil 0) m)))
(deftest test-nested-map-destructuring
(let [sample-map {:a 1 :b {:a 2}}
{ao1 :a {ai1 :a} :b} sample-map
{ao2 :a {ai2 :a :as m1} :b :as m2} sample-map
{ao3 :a {ai3 :a :as m} :b :as m} sample-map
{{ai4 :a :as m} :b ao4 :a :as m} sample-map]
(are [i o] (and (= i 2)
(= o 1))
ai1 ao1
ai2 ao2
ai3 ao3
ai4 ao4)))
;; *** Sets ***
(deftest test-hash-set
(are [x] (set? x)
#{}
#{1 2}
(hash-set)
(hash-set 1 2) )
; order isn't important
(are [x y] (= x y)
#{1 2} #{2 1}
#{3 1 2} #{1 2 3}
(hash-set 1 2) (hash-set 2 1)
(hash-set 3 1 2) (hash-set 1 2 3) )
(are [x y] (= x y)
; equal classes
(class #{}) (class (hash-set))
(class #{1 2}) (class (hash-set 1 2))
; creating
(hash-set) #{}
(hash-set 1) #{1}
(hash-set 1 2) #{1 2}
; nesting
(hash-set 1 (hash-set 2 3) (hash-set 3 (hash-set 4 5 (hash-set 6 (hash-set 7)))))
#{1 #{2 3} #{3 #{4 5 #{6 #{7}}}}}
; different data structures
(hash-set true false nil)
#{true false nil}
(hash-set 1 2.5 2/3 "ab" \x 'cd :kw)
#{1 2.5 2/3 "ab" \x 'cd :kw}
(hash-set (list 1 2) [3 4] {:a 1 :b 2} #{:c :d})
#{'(1 2) [3 4] {:a 1 :b 2} #{:c :d}}
; evaluation
(hash-set (+ 1 2) [(+ 2 3) :a] (hash-set (* 2 3) 8))
#{3 [5 :a] #{6 8}}
; special cases
(hash-set nil) #{nil}
(hash-set 1 nil) #{1 nil}
(hash-set nil 2) #{nil 2}
(hash-set #{}) #{#{}}
(hash-set 1 #{}) #{1 #{}}
(hash-set #{} 2) #{#{} 2}
(hash-set (Integer. -1)) (hash-set (Long. -1))))
(deftest test-sorted-set
; only compatible types can be used
(is (thrown? ClassCastException (sorted-set 1 "a")))
(is (thrown? ClassCastException (sorted-set '(1 2) [3 4])))
; creates set?
(are [x] (set? x)
(sorted-set)
(sorted-set 1 2) )
; equal and unique
(are [x] (and (= (sorted-set x) #{x})
(= (sorted-set x x) (sorted-set x)))
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() ; '(1 2)
[] [1 2]
{} ; {:a 1 :b 2}
#{} ; #{1 2}
)
; cannot be cast to java.lang.Comparable
(is (thrown? ClassCastException (sorted-set '(1 2) '(1 2))))
(is (thrown? ClassCastException (sorted-set {:a 1 :b 2} {:a 1 :b 2})))
(is (thrown? ClassCastException (sorted-set #{1 2} #{1 2})))
(are [x y] (= x y)
; generating
(sorted-set) #{}
(sorted-set 1) #{1}
(sorted-set 1 2) #{1 2}
; sorting
(seq (sorted-set 5 4 3 2 1)) '(1 2 3 4 5)
; special cases
(sorted-set nil) #{nil}
(sorted-set 1 nil) #{nil 1}
(sorted-set nil 2) #{nil 2}
(sorted-set #{}) #{#{}} ))
(deftest test-sorted-set-by
; only compatible types can be used
; NB: not a ClassCastException, but a RuntimeException is thrown,
; requires discussion on whether this should be symmetric with test-sorted-set
(is (thrown? Exception (sorted-set-by < 1 "a")))
(is (thrown? Exception (sorted-set-by < '(1 2) [3 4])))
; creates set?
(are [x] (set? x)
(sorted-set-by <)
(sorted-set-by < 1 2) )
; equal and unique
(are [x] (and (= (sorted-set-by compare x) #{x})
(= (sorted-set-by compare x x) (sorted-set-by compare x)))
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() ; '(1 2)
[] [1 2]
{} ; {:a 1 :b 2}
#{} ; #{1 2}
)
; cannot be cast to java.lang.Comparable
; NB: not a ClassCastException, but a RuntimeException is thrown,
; requires discussion on whether this should be symmetric with test-sorted-set
(is (thrown? Exception (sorted-set-by compare '(1 2) '(1 2))))
(is (thrown? Exception (sorted-set-by compare {:a 1 :b 2} {:a 1 :b 2})))
(is (thrown? Exception (sorted-set-by compare #{1 2} #{1 2})))
(are [x y] (= x y)
; generating
(sorted-set-by >) #{}
(sorted-set-by > 1) #{1}
(sorted-set-by > 1 2) #{1 2}
; sorting
(seq (sorted-set-by < 5 4 3 2 1)) '(1 2 3 4 5)
; special cases
(sorted-set-by compare nil) #{nil}
(sorted-set-by compare 1 nil) #{nil 1}
(sorted-set-by compare nil 2) #{nil 2}
(sorted-set-by compare #{}) #{#{}} ))
(deftest test-set
; set?
(are [x] (set? (set x))
() '(1 2)
[] [1 2]
#{} #{1 2}
{} {:a 1 :b 2}
(into-array []) (into-array [1 2])
"" "abc" )
; unique
(are [x] (= (set [x x]) #{x})
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2} )
; conversion
(are [x y] (= (set x) y)
() #{}
'(1 2) #{1 2}
[] #{}
[1 2] #{1 2}
#{} #{} ; identity
#{1 2} #{1 2} ; identity
{} #{}
{:a 1 :b 2} #{[:a 1] [:b 2]}
(into-array []) #{}
(into-array [1 2]) #{1 2}
"" #{}
"abc" #{\a \b \c} ))
(deftest test-disj
; doesn't work on lists, vectors or maps
(is (thrown? ClassCastException (disj '(1 2) 1)))
(is (thrown? ClassCastException (disj [1 2] 1)))
(is (thrown? ClassCastException (disj {:a 1} :a)))
; identity
(are [x] (= (disj x) x)
nil
#{}
#{1 2 3}
; different data types
#{nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2}} )
; type identity
(are [x] (= (class (disj x)) (class x))
(hash-set)
(hash-set 1 2)
(sorted-set)
(sorted-set 1 2) )
(are [x y] (= x y)
(disj nil :a) nil
(disj nil :a :b) nil
(disj #{} :a) #{}
(disj #{} :a :b) #{}
(disj #{:a} :a) #{}
(disj #{:a} :a :b) #{}
(disj #{:a} :c) #{:a}
(disj #{:a :b :c :d} :a) #{:b :c :d}
(disj #{:a :b :c :d} :a :d) #{:b :c}
(disj #{:a :b :c :d} :a :b :c) #{:d}
(disj #{:a :b :c :d} :d :a :c :b) #{}
(disj #{nil} :a) #{nil}
(disj #{nil} #{}) #{nil}
(disj #{nil} nil) #{}
(disj #{#{}} nil) #{#{}}
(disj #{#{}} #{}) #{}
(disj #{#{nil}} #{nil}) #{} ))
;; *** Queues ***
(deftest test-queues
(let [EMPTY clojure.lang.PersistentQueue/EMPTY]
(are [x y] (= x y)
EMPTY EMPTY
(into EMPTY (range 50)) (into EMPTY (range 50))
(conj EMPTY (Long. -1)) (conj EMPTY (Integer. -1))
(hash (conj EMPTY (Long. -1))) (hash (conj EMPTY (Integer. -1)))
(hash [1 2 3]) (hash (conj EMPTY 1 2 3))
(range 5) (into EMPTY (range 5))
(range 1 6) (-> EMPTY
(into (range 6))
pop))
(are [x y] (not= x y)
(range 5) (into EMPTY (range 6))
(range 6) (into EMPTY (range 5))
(range 0 6) (-> EMPTY
(into (range 6))
pop)
(range 1 6) (-> EMPTY
(into (range 7))
pop))))
(deftest test-duplicates
(let [equal-sets-incl-meta (fn [s1 s2]
(and (= s1 s2)
(let [ss1 (sort s1)
ss2 (sort s2)]
(every? identity
(map #(and (= %1 %2)
(= (meta %1) (meta %2)))
ss1 ss2)))))
all-equal-sets-incl-meta (fn [& ss]
(every? (fn [[s1 s2]]
(equal-sets-incl-meta s1 s2))
(partition 2 1 ss)))
equal-maps-incl-meta (fn [m1 m2]
(and (= m1 m2)
(equal-sets-incl-meta (set (keys m1))
(set (keys m2)))
(every? #(= (meta (m1 %)) (meta (m2 %)))
(keys m1))))
all-equal-maps-incl-meta (fn [& ms]
(every? (fn [[m1 m2]]
(equal-maps-incl-meta m1 m2))
(partition 2 1 ms)))
cmp-first #(> (first %1) (first %2))
x1 (with-meta [1] {:me "x"})
y2 (with-meta [2] {:me "y"})
z3a (with-meta [3] {:me "z3a"})
z3b (with-meta [3] {:me "z3b"})
v4a (with-meta [4] {:me "v4a"})
v4b (with-meta [4] {:me "v4b"})
v4c (with-meta [4] {:me "v4c"})
w5a (with-meta [5] {:me "w5a"})
w5b (with-meta [5] {:me "w5b"})
w5c (with-meta [5] {:me "w5c"})]
;; Sets
(is (thrown? IllegalArgumentException
(read-string "#{1 2 3 4 1 5}")))
;; If there are duplicate items when doing (conj #{} x1 x2 ...),
;; the behavior is that the metadata of the first item is kept.
(are [s x] (all-equal-sets-incl-meta s
(apply conj #{} x)
(set x)
(apply hash-set x)
(apply sorted-set x)
(apply sorted-set-by cmp-first x))
#{x1 y2} [x1 y2]
#{x1 z3a} [x1 z3a z3b]
#{w5b} [w5b w5a w5c]
#{z3a x1} [z3a z3b x1])
;; Maps
(is (thrown? IllegalArgumentException
(read-string "{:a 1, :b 2, :a -1, :c 3}")))
;; If there are duplicate keys when doing (assoc {} k1 v1 k2 v2
;; ...), the behavior is that the metadata of the first duplicate
;; key is kept, but mapped to the last value with an equal key
;; (where metadata of keys are not compared).
(are [h x] (all-equal-maps-incl-meta h
(apply assoc {} x)
(apply hash-map x)
(apply sorted-map x)
(apply sorted-map-by cmp-first x)
(apply array-map x))
{x1 2, z3a 4} [x1 2, z3a 4]
{x1 2, z3a 5} [x1 2, z3a 4, z3b 5]
{z3a 5} [z3a 2, z3a 4, z3b 5]
{z3b 4, x1 5} [z3b 2, z3a 4, x1 5]
{z3b v4b, x1 5} [z3b v4a, z3a v4b, x1 5]
{x1 v4a, w5a v4c, v4a z3b, y2 2} [x1 v4a, w5a v4a, w5b v4b,
v4a z3a, y2 2, v4b z3b, w5c v4c])))
(deftest test-assoc
(are [x y] (= x y)
[4] (assoc [] 0 4)
[5 -7] (assoc [] 0 5 1 -7)
{:a 1} (assoc {} :a 1)
{nil 1} (assoc {} nil 1)
{:a 2 :b -2} (assoc {} :b -2 :a 2))
(is (thrown? IllegalArgumentException (assoc [] 0 5 1)))
(is (thrown? IllegalArgumentException (assoc {} :b -2 :a))))
(defn is-same-collection [a b]
(let [msg (format "(class a)=%s (class b)=%s a=%s b=%s"
(.getName (class a)) (.getName (class b)) a b)]
(is (= (count a) (count b) (.size a) (.size b)) msg)
(is (= a b) msg)
(is (= b a) msg)
(is (.equals ^Object a b) msg)
(is (.equals ^Object b a) msg)
(is (= (hash a) (hash b)) msg)
(is (= (.hashCode ^Object a) (.hashCode ^Object b)) msg)))
(deftest ordered-collection-equality-test
(let [empty-colls [ []
'()
(lazy-seq)
clojure.lang.PersistentQueue/EMPTY
(vector-of :long) ]]
(doseq [c1 empty-colls, c2 empty-colls]
(is-same-collection c1 c2)))
(let [colls1 [ [-3 :a "7th"]
'(-3 :a "7th")
(lazy-seq (cons -3
(lazy-seq (cons :a
(lazy-seq (cons "7th" nil))))))
(into clojure.lang.PersistentQueue/EMPTY
[-3 :a "7th"])
(sequence (map identity) [-3 :a "7th"]) ]]
(doseq [c1 colls1, c2 colls1]
(is-same-collection c1 c2)))
(is-same-collection [-3 1 7] (vector-of :long -3 1 7)))
(defn case-indendent-string-cmp [s1 s2]
(compare (string/lower-case s1) (string/lower-case s2)))
(deftest set-equality-test
(let [empty-sets [ #{}
(hash-set)
(sorted-set)
(sorted-set-by case-indendent-string-cmp) ]]
(doseq [s1 empty-sets, s2 empty-sets]
(is-same-collection s1 s2)))
(let [sets1 [ #{"Banana" "apple" "7th"}
(hash-set "Banana" "apple" "7th")
(sorted-set "Banana" "apple" "7th")
(sorted-set-by case-indendent-string-cmp "Banana" "apple" "7th") ]]
(doseq [s1 sets1, s2 sets1]
(is-same-collection s1 s2))))
(deftest map-equality-test
(let [empty-maps [ {}
(hash-map)
(array-map)
(sorted-map)
(sorted-map-by case-indendent-string-cmp) ]]
(doseq [m1 empty-maps, m2 empty-maps]
(is-same-collection m1 m2)))
(let [maps1 [ {"Banana" "like", "apple" "love", "7th" "indifferent"}
(hash-map "Banana" "like", "apple" "love", "7th" "indifferent")
(array-map "Banana" "like", "apple" "love", "7th" "indifferent")
(sorted-map "Banana" "like", "apple" "love", "7th" "indifferent")
(sorted-map-by case-indendent-string-cmp
"Banana" "like", "apple" "love", "7th" "indifferent") ]]
(doseq [m1 maps1, m2 maps1]
(is-same-collection m1 m2))))
;; *** Collection hashes ***
;; See: http://clojure.org/data_structures#hash
(defn hash-ordered [collection]
(-> (reduce (fn [acc e] (unchecked-add-int (unchecked-multiply-int 31 acc) (hash e)))
1
collection)
(mix-collection-hash (count collection))))
(defn hash-unordered [collection]
(-> (reduce unchecked-add-int 0 (map hash collection))
(mix-collection-hash (count collection))))
(defn gen-elements
[]
(gen/vec gen/anything))
(defspec ordered-collection-hashes-match
identity
[^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
(let [v (vec elem)
l (apply list elem)]
(is (= (hash v)
(hash l)
(hash (map identity elem))
(hash-ordered elem)))))
(defspec unordered-set-hashes-match
identity
[^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
(let [unique-elem (distinct elem)
s (into #{} unique-elem)]
(is (= (hash s)
(hash-unordered unique-elem)))))
(deftest ireduce-reduced
(let [f (fn [_ a] (if (= a 5) (reduced "foo")))]
(is (= "foo" (.reduce ^clojure.lang.IReduce (list 1 2 3 4 5) f)))
(is (= "foo" (.reduce ^clojure.lang.IReduce (seq (long-array [1 2 3 4 5])) f)))))
(defn seq-iter-match
[^clojure.lang.Seqable seqable ^Iterable iterable]
(if (nil? iterable)
(when (not (nil? (seq seqable)))
(throw (ex-info "Null iterable but seq has elements"
{:pos 0 :seqable seqable :iterable iterable})))
(let [i (.iterator iterable)]
(loop [s (seq seqable)
n 0]
(if (seq s)
(do
(when-not (.hasNext i)
(throw (ex-info "Iterator exhausted before seq"
{:pos n :seqable seqable :iterable iterable})))
(when-not (= (.next i) (first s))
(throw (ex-info "Iterator and seq did not match"
{:pos n :seqable seqable :iterable iterable})))
(recur (rest s) (inc n)))
(when (.hasNext i)
(throw (ex-info "Seq exhausted before iterator"
{:pos n :seqable seqable :iterable iterable}))))))))
(deftest test-seq-iter-match
(let [maps (mapcat #(vector (apply array-map %)
(apply hash-map %)
(apply sorted-map %))
[[] [nil 1] [nil 1 2 3] [1 2 3 4]])]
(doseq [m maps]
(seq-iter-match m m)
(seq-iter-match (keys m) (keys m))
(seq-iter-match (vals m) (vals m))
(seq-iter-match (rest (keys m)) (rest (keys m)))
(seq-iter-match (rest (vals m)) (rest (vals m))))))
(defn gen-map
[]
(gen/hash-map (rand-nth cgen/ednable-scalars) (rand-nth cgen/ednable-scalars)))
(defspec seq-and-iter-match-for-maps
identity
[^{:tag clojure.test-clojure.data-structures/gen-map} m]
(seq-iter-match m m))
(defn gen-set
[]
(gen/set (rand-nth cgen/ednable-scalars)))
(defspec seq-and-iter-match-for-sets
identity
[^{:tag clojure.test-clojure.data-structures/gen-set} s]
(seq-iter-match s s))
(defn gen-queue
[]
(into clojure.lang.PersistentQueue/EMPTY
(gen/vec (rand-nth cgen/ednable-scalars))))
(defspec seq-and-iter-match-for-queues
identity
[^{:tag clojure.test-clojure.data-structures/gen-queue} q]
(seq-iter-match q q))
(defrecord Rec [a b])
(defn gen-record
[]
(let [r (->Rec (gen/int) (gen/int))]
(gen/one-of r
(merge r (gen-map)))))
(defspec seq-and-iter-match-for-records
identity
[^{:tag clojure.test-clojure.data-structures/gen-record} r]
(seq-iter-match r r))
(defspec seq-and-iter-match-for-keys
identity
[^{:tag clojure.test-clojure.data-structures/gen-map} m]
(seq-iter-match (keys m) (keys m)))
(defspec seq-and-iter-match-for-vals
identity
[^{:tag clojure.test-clojure.data-structures/gen-map} m]
(seq-iter-match (vals m) (vals m)))
(defstruct test-struct :a :b)
(defn gen-struct
[]
(let [s (struct test-struct (gen/int) (gen/int))]
(gen/one-of s
(assoc s (rand-nth cgen/ednable-scalars) (rand-nth cgen/ednable-scalars)))))
(defspec seq-and-iter-match-for-structs
identity
[^{:tag clojure.test-clojure.data-structures/gen-struct} s]
(seq-iter-match s s))
================================================
FILE: test/clojure/test_clojure/def.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.def
(:use clojure.test clojure.test-helper
clojure.test-clojure.protocols))
(deftest defn-error-messages
(testing "multiarity syntax invalid parameter declaration"
(is (fails-with-cause?
IllegalArgumentException
#"Parameter declaration \"arg1\" should be a vector"
(eval-in-temp-ns (defn foo (arg1 arg2))))))
(testing "multiarity syntax invalid signature"
(is (fails-with-cause?
IllegalArgumentException
#"Invalid signature \"\[a b\]\" should be a list"
(eval-in-temp-ns (defn foo
([a] 1)
[a b])))))
(testing "assume single arity syntax"
(is (fails-with-cause?
IllegalArgumentException
#"Parameter declaration \"a\" should be a vector"
(eval-in-temp-ns (defn foo a)))))
(testing "bad name"
(is (fails-with-cause?
IllegalArgumentException
#"First argument to defn must be a symbol"
(eval-in-temp-ns (defn "bad docstring" testname [arg1 arg2])))))
(testing "missing parameter/signature"
(is (fails-with-cause?
IllegalArgumentException
#"Parameter declaration missing"
(eval-in-temp-ns (defn testname)))))
(testing "allow trailing map"
(is (eval-in-temp-ns (defn a "asdf" ([a] 1) {:a :b}))))
(testing "don't allow interleaved map"
(is (fails-with-cause?
IllegalArgumentException
#"Invalid signature \"\{:a :b\}\" should be a list"
(eval-in-temp-ns (defn a "asdf" ([a] 1) {:a :b} ([] 1)))))))
(deftest non-dynamic-warnings
(testing "no warning for **"
(is (empty? (with-err-print-writer
(eval-in-temp-ns (defn ** ([a b] (Math/pow (double a) (double b)))))))))
(testing "warning for *hello*"
(is (not (empty? (with-err-print-writer
(eval-in-temp-ns (def *hello* "hi"))))))))
(deftest dynamic-redefinition
;; too many contextual things for this kind of caching to work...
(testing "classes are never cached, even if their bodies are the same"
(is (= :b
(eval
'(do
(defmacro my-macro [] :a)
(defn do-macro [] (my-macro))
(defmacro my-macro [] :b)
(defn do-macro [] (my-macro))
(do-macro)))))))
(deftest nested-dynamic-declaration
(testing "vars :dynamic meta data is applied immediately to vars declared anywhere"
(is (= 10
(eval
'(do
(list
(declare ^:dynamic p)
(defn q [] @p))
(binding [p (atom 10)]
(q))))))))
================================================
FILE: test/clojure/test_clojure/delays.clj
================================================
(ns clojure.test-clojure.delays
(:use clojure.test))
(deftest calls-once
(let [a (atom 0)
d (delay (swap! a inc))]
(is (= 0 @a))
(is (= 1 @d))
(is (= 1 @d))
(is (= 1 @a))))
(deftest saves-exceptions
(let [f #(do (throw (Exception. "broken"))
1)
d (delay (f))
try-call #(try
@d
(catch Exception e e))
first-result (try-call)]
(is (instance? Exception first-result))
(is (identical? first-result (try-call)))))
================================================
FILE: test/clojure/test_clojure/edn.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Stuart Halloway
(ns clojure.test-clojure.edn
(:require [clojure.test.generative :refer (defspec)]
[clojure.test-clojure.generators :as cgen]
[clojure.edn :as edn]))
(defn roundtrip
"Print an object and read it back as edn. Returns rather than throws
any exceptions."
[o]
(binding [*print-length* nil
*print-dup* nil
*print-level* nil]
(try
(-> o pr-str edn/read-string)
(catch Throwable t t))))
(defspec types-that-should-roundtrip
roundtrip
[^{:tag cgen/ednable} o]
(when-not (= o %)
(throw (ex-info "Value cannot roundtrip, see ex-data" {:printed o :read %}))))
(defspec types-that-should-not-roundtrip
roundtrip
[^{:tag cgen/non-ednable} o]
(when-not (instance? Throwable %)
(throw (ex-info "edn/read should have thrown, see ex-data" {:printed o :read %}))))
================================================
FILE: test/clojure/test_clojure/errors.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Tests for error handling and messages
(ns clojure.test-clojure.errors
(:use clojure.test)
(:import clojure.lang.ArityException))
(defn f0 [] 0)
(defn f1 [a] a)
;; Function name that includes many special characters to test demunge
(defn f2:+><->!#%&*|b [x] x)
(defmacro m0 [] `(identity 0))
(defmacro m1 [a] `(inc ~a))
(deftest arity-exception
;; IllegalArgumentException is pre-1.3
(is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(1\) passed to"
(f0 1)))
(is (thrown-with-msg? ArityException #"Wrong number of args \(0\) passed to"
(f1)))
(is (thrown-with-msg? ArityException #"Wrong number of args \(1\) passed to"
(macroexpand `(m0 1))))
(is (thrown-with-msg? ArityException #"Wrong number of args \(2\) passed to"
(macroexpand `(m1 1 2))))
(is (thrown-with-msg? ArityException #"\Qerrors-f2:+><->!#%&*|b\E"
(f2:+><->!#%&*|b 1 2))
"ArityException messages should demunge function names"))
(deftest assert-arg-messages
; used to ensure that error messages properly use local names for macros
(refer 'clojure.core :rename '{with-open renamed-with-open})
; would have used `are` here, but :line meta on &form doesn't survive successive macroexpansions
(doseq [[msg-regex-str form] [["if-let .* in %s:\\d+" '(if-let [a 5
b 6]
true nil)]
["let .* in %s:\\d+" '(let [a])]
["let .* in %s:\\d+" '(let (a))]
["renamed-with-open .* in %s:\\d+" '(renamed-with-open [a])]]]
(is (thrown-with-msg? IllegalArgumentException
(re-pattern (format msg-regex-str *ns*))
(macroexpand form)))))
(deftest extract-ex-data
(try
(throw (ex-info "example error" {:foo 1}))
(catch Throwable t
(is (= {:foo 1} (ex-data t)))))
(is (nil? (ex-data (RuntimeException. "example non ex-data")))))
(deftest Throwable->map-test
(testing "base functionality"
(let [{:keys [cause via trace]} (Throwable->map
(Exception. "I am a string literal"))]
(is (= cause "I am a string literal"))
(is (= 1 (count via)))
(is (vector? via))
(is (= ["I am a string literal"] (map :message via)))))
(testing "causes"
(let [{:keys [cause via trace]} (Throwable->map
(Exception. "I am not a number"
(Exception. "double two")))]
(is (= cause "double two"))
(is (= ["I am not a number" "double two"]
(map :message via)))))
(testing "ex-data"
(let [{[{:keys [data]}] :via
data-top-level :data}
(Throwable->map (ex-info "ex-info"
{:some "data"}))]
(is (= data data-top-level {:some "data"})))))
================================================
FILE: test/clojure/test_clojure/evaluation.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Tests for the Clojure functions documented at the URL:
;;
;; http://clojure.org/Evaluation
;;
;; by J. McConnell
;; Created 22 October 2008
(ns clojure.test-clojure.evaluation
(:use clojure.test))
(import '(java.lang Boolean)
'(clojure.lang Compiler Compiler$CompilerException))
(defmacro test-that
"Provides a useful way for specifying the purpose of tests. If the first-level
forms are lists that make a call to a clojure.test function, it supplies the
purpose as the msg argument to those functions. Otherwise, the purpose just
acts like a comment and the forms are run unchanged."
[purpose & test-forms]
(let [tests (map
#(if (= (:ns (meta (resolve (first %))))
(the-ns 'clojure.test))
(concat % (list purpose))
%)
test-forms)]
`(do ~@tests)))
(deftest Eval
(is (= (eval '(+ 1 2 3)) (Compiler/eval '(+ 1 2 3))))
(is (= (eval '(list 1 2 3)) '(1 2 3)))
(is (= (eval '(list + 1 2 3)) (list clojure.core/+ 1 2 3)))
(test-that "Non-closure fns are supported as code"
(is (= (eval (eval '(list + 1 2 3))) 6)))
(is (= (eval (list '+ 1 2 3)) 6)))
; not using Clojure's RT/classForName since a bug in it could hide a bug in
; eval's resolution
(defn class-for-name [name]
(java.lang.Class/forName name))
(defmacro in-test-ns [& body]
`(binding [*ns* *ns*]
(in-ns 'clojure.test-clojure.evaluation)
~@body))
;;; Literals tests ;;;
(defmacro #^{:private true} evaluates-to-itself? [expr]
`(let [v# ~expr
q# (quote ~expr)]
(is (= (eval q#) q#) (str q# " does not evaluate to itself"))))
(deftest Literals
; Strings, numbers, characters, nil and keywords should evaluate to themselves
(evaluates-to-itself? "test")
(evaluates-to-itself? "test
multi-line
string")
(evaluates-to-itself? 1)
(evaluates-to-itself? 1.0)
(evaluates-to-itself? 1.123456789)
(evaluates-to-itself? 1/2)
(evaluates-to-itself? 1M)
(evaluates-to-itself? 999999999999999999)
(evaluates-to-itself? \a)
(evaluates-to-itself? \newline)
(evaluates-to-itself? nil)
(evaluates-to-itself? :test)
; Boolean literals should evaluate to Boolean.{TRUE|FALSE}
(is (identical? (eval true) Boolean/TRUE))
(is (identical? (eval false) Boolean/FALSE)))
;;; Symbol resolution tests ;;;
(def foo "abc")
(in-ns 'resolution-test)
(def bar 123)
(def #^{:private true} baz 456)
(in-ns 'clojure.test-clojure.evaluation)
(defn a-match? [re s] (not (nil? (re-matches re s))))
(defmacro throws-with-msg
([re form] `(throws-with-msg ~re ~form Exception))
([re form x] `(throws-with-msg
~re
~form
~(if (instance? Exception x) x Exception)
~(if (instance? String x) x nil)))
([re form class msg]
`(let [ex# (try
~form
(catch ~class e# e#)
(catch Exception e#
(let [cause# (.getCause e#)]
(if (= ~class (class cause#)) cause# (throw e#)))))]
(is (a-match? ~re (.toString ex#))
(or ~msg
(str "Expected exception that matched " (pr-str ~re)
", but got exception with message: \"" ex#))))))
(deftest SymbolResolution
(test-that
"If a symbol is namespace-qualified, the evaluated value is the value
of the binding of the global var named by the symbol"
(is (= (eval 'resolution-test/bar) 123)))
(test-that
"It is an error if there is no global var named by the symbol"
(throws-with-msg
#".*Unable to resolve symbol: bar.*" (eval 'bar)))
(test-that
"It is an error if the symbol reference is to a non-public var in a
different namespace"
(throws-with-msg
#".*resolution-test/baz is not public.*"
(eval 'resolution-test/baz)
Compiler$CompilerException))
(test-that
"If a symbol is package-qualified, its value is the Java class named by the
symbol"
(is (= (eval 'java.lang.Math) (class-for-name "java.lang.Math"))))
(test-that
"If a symbol is package-qualified, it is an error if there is no Class named
by the symbol"
(is (thrown? Compiler$CompilerException (eval 'java.lang.FooBar))))
(test-that
"If a symbol is not qualified, the following applies, in this order:
1. If it names a special form it is considered a special form, and must
be utilized accordingly.
2. A lookup is done in the current namespace to see if there is a mapping
from the symbol to a class. If so, the symbol is considered to name a
Java class object.
3. If in a local scope (i.e. in a function definition), a lookup is done
to see if it names a local binding (e.g. a function argument or
let-bound name). If so, the value is the value of the local binding.
4. A lookup is done in the current namespace to see if there is a mapping
from the symbol to a var. If so, the value is the value of the binding
of the var referred-to by the symbol.
5. It is an error."
; First
(doall (for [form '(def if do let quote var fn loop recur throw try
monitor-enter monitor-exit)]
(is (thrown? Compiler$CompilerException (eval form)))))
(let [if "foo"]
(is (thrown? Compiler$CompilerException (eval 'if)))
; Second
(is (= (eval 'Boolean) (class-for-name "java.lang.Boolean"))))
(let [Boolean "foo"]
(is (= (eval 'Boolean) (class-for-name "java.lang.Boolean"))))
; Third
(is (= (eval '(let [foo "bar"] foo)) "bar"))
; Fourth
(in-test-ns (is (= (eval 'foo) "abc")))
(is (thrown? Compiler$CompilerException (eval 'bar))) ; not in this namespace
; Fifth
(is (thrown? Compiler$CompilerException (eval 'foobar)))))
;;; Metadata tests ;;;
(defstruct struct-with-symbols (with-meta 'k {:a "A"}))
(deftest Metadata
(test-that
"find returns key symbols and their metadata"
(let [s (struct struct-with-symbols 1)]
(is (= {:a "A"} (meta (first (find s 'k))))))))
;;; Collections tests ;;;
(def x 1)
(def y 2)
(deftest Collections
(in-test-ns
(test-that
"Vectors and Maps yield vectors and (hash) maps whose contents are the
evaluated values of the objects they contain."
(is (= (eval '[x y 3]) [1 2 3]))
(is (= (eval '{:x x :y y :z 3}) {:x 1 :y 2 :z 3}))
(is (instance? clojure.lang.IPersistentMap (eval '{:x x :y y})))))
(in-test-ns
(test-that
"Metadata maps yield maps whose contents are the evaluated values of
the objects they contain. If a vector or map has metadata, the evaluated
metadata map will become the metadata of the resulting value."
(is (= (eval #^{:x x} '[x y]) #^{:x 1} [1 2]))))
(test-that
"An empty list () evaluates to an empty list."
(is (= (eval '()) ()))
(is (empty? (eval ())))
(is (= (eval (list)) ())))
;aargh, fragile tests, please fix
#_(test-that
"Non-empty lists are considered calls"
(is (thrown? Compiler$CompilerException (eval '(1 2 3))))))
(deftest Macros)
(deftest Loading)
================================================
FILE: test/clojure/test_clojure/fn.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Ambrose Bonnaire-Sergeant
(ns clojure.test-clojure.fn
(:use clojure.test))
(deftest fn-error-checking
(testing "bad arglist"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration a should be a vector"
(eval '(fn "a" a)))))
(testing "treat first param as args"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration a should be a vector"
(eval '(fn "a" [])))))
(testing "looks like listy signature, but malformed declaration"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration 1 should be a vector"
(eval '(fn (1))))))
(testing "checks each signature"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration a should be a vector"
(eval '(fn
([a] 1)
("a" 2))))))
(testing "correct name but invalid args"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration a should be a vector"
(eval '(fn a "a")))))
(testing "first sig looks multiarity, rest of sigs should be lists"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Invalid signature \[a b\] should be a list"
(eval '(fn a
([a] 1)
[a b])))))
(testing "missing parameter declaration"
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration missing"
(eval '(fn a))))
(is (fails-with-cause? java.lang.IllegalArgumentException
#"Parameter declaration missing"
(eval '(fn))))))
================================================
FILE: test/clojure/test_clojure/for.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Tests for the Clojure 'for' macro
;;
;; by Chouser
;; Created Dec 2008
(ns clojure.test-clojure.for
(:use clojure.test))
(deftest Docstring-Example
(is (= (take 100 (for [x (range 100000000)
y (range 1000000) :while (< y x)]
[x y]))
'([1 0] [2 0] [2 1] [3 0] [3 1] [3 2] [4 0] [4 1] [4 2] [4 3]
[5 0] [5 1] [5 2] [5 3] [5 4]
[6 0] [6 1] [6 2] [6 3] [6 4] [6 5]
[7 0] [7 1] [7 2] [7 3] [7 4] [7 5] [7 6]
[8 0] [8 1] [8 2] [8 3] [8 4] [8 5] [8 6] [8 7]
[9 0] [9 1] [9 2] [9 3] [9 4] [9 5] [9 6] [9 7] [9 8]
[10 0] [10 1] [10 2] [10 3] [10 4] [10 5] [10 6] [10 7] [10 8] [10 9]
[11 0] [11 1] [11 2] [11 3] [11 4] [11 5] [11 6] [11 7] [11 8] [11 9]
[11 10]
[12 0] [12 1] [12 2] [12 3] [12 4] [12 5] [12 6] [12 7] [12 8] [12 9]
[12 10] [12 11]
[13 0] [13 1] [13 2] [13 3] [13 4] [13 5] [13 6] [13 7] [13 8] [13 9]
[13 10] [13 11] [13 12]
[14 0] [14 1] [14 2] [14 3] [14 4] [14 5] [14 6] [14 7] [14 8]))))
(defmacro deftest-both [txt & ises]
`(do
(deftest ~(symbol (str "For-" txt)) ~@ises)
(deftest ~(symbol (str "Doseq-" txt))
~@(map (fn [[x-is [x-= [x-for binds body] value]]]
(when (and (= x-is 'is) (= x-= '=) (= x-for 'for))
`(is (= (let [acc# (atom [])]
(doseq ~binds (swap! acc# conj ~body))
@acc#)
~value))))
ises))))
(deftest-both When
(is (= (for [x (range 10) :when (odd? x)] x) '(1 3 5 7 9)))
(is (= (for [x (range 4) y (range 4) :when (odd? y)] [x y])
'([0 1] [0 3] [1 1] [1 3] [2 1] [2 3] [3 1] [3 3])))
(is (= (for [x (range 4) y (range 4) :when (odd? x)] [x y])
'([1 0] [1 1] [1 2] [1 3] [3 0] [3 1] [3 2] [3 3])))
(is (= (for [x (range 4) :when (odd? x) y (range 4)] [x y])
'([1 0] [1 1] [1 2] [1 3] [3 0] [3 1] [3 2] [3 3])))
(is (= (for [x (range 5) y (range 5) :when (< x y)] [x y])
'([0 1] [0 2] [0 3] [0 4] [1 2] [1 3] [1 4] [2 3] [2 4] [3 4]))))
(defn only
"Returns a lazy seq of increasing ints starting at 0. Trying to get
the nth+1 value of the seq throws an exception. This is meant to
help detecting over-eagerness in lazy seq consumers."
[n]
(lazy-cat (range n)
(throw (Exception. "consumer went too far in lazy seq"))))
(deftest-both While
(is (= (for [x (only 6) :while (< x 5)] x) '(0 1 2 3 4)))
(is (= (for [x (range 4) y (only 4) :while (< y 3)] [x y])
'([0 0] [0 1] [0 2] [1 0] [1 1] [1 2]
[2 0] [2 1] [2 2] [3 0] [3 1] [3 2])))
(is (= (for [x (range 4) y (range 4) :while (< x 3)] [x y])
'([0 0] [0 1] [0 2] [0 3] [1 0] [1 1] [1 2] [1 3]
[2 0] [2 1] [2 2] [2 3])))
(is (= (for [x (only 4) :while (< x 3) y (range 4)] [x y])
'([0 0] [0 1] [0 2] [0 3] [1 0] [1 1] [1 2] [1 3]
[2 0] [2 1] [2 2] [2 3])))
(is (= (for [x (range 4) y (range 4) :while (even? x)] [x y])
'([0 0] [0 1] [0 2] [0 3] [2 0] [2 1] [2 2] [2 3])))
(is (= (for [x (only 2) :while (even? x) y (range 4)] [x y])
'([0 0] [0 1] [0 2] [0 3])))
(is (= (for [x (range 4) y (only 4) :while (< y x)] [x y])
'([1 0] [2 0] [2 1] [3 0] [3 1] [3 2]))))
(deftest-both While-and-When
(is (= (for [x (only 6) :while (< x 5) y (range 4) :when (odd? y)] [x y])
'([0 1] [0 3] [1 1] [1 3] [2 1] [2 3] [3 1] [3 3] [4 1] [4 3])))
(is (= (for [x (range 4) :when (odd? x) y (only 6) :while (< y 5)] [x y])
'([1 0] [1 1] [1 2] [1 3] [1 4] [3 0] [3 1] [3 2] [3 3] [3 4])))
(is (= (for [x (only 6) :while (< x 5) y (range 4) :when (odd? (+ x y))]
[x y])
'([0 1] [0 3] [1 0] [1 2] [2 1] [2 3] [3 0] [3 2] [4 1] [4 3])))
(is (= (for [x (range 4) :when (odd? x) y (only 2) :while (odd? (+ x y))]
[x y])
'([1 0] [3 0]))))
(deftest-both While-and-When-Same-Binding
(is (= (for [x (only 6) :while (< x 5) :when (odd? x)] x) '(1 3)))
(is (= (for [x (only 6)
:while (< x 5) ; if :while is false, :when should not be evaled
:when (do (if (< x 5) (odd? x)))] x) '(1 3)))
(is (= (for [a (range -2 5)
:when (not= a 0) ; :when may guard :while
:while (> (Math/abs (/ 1.0 a)) 1/3)] a) '(-2 -1 1 2))))
(deftest-both Nesting
(is (= (for [x '(a b) y (interpose x '(1 2)) z (list x y)] [x y z])
'([a 1 a] [a 1 1] [a a a] [a a a] [a 2 a] [a 2 2]
[b 1 b] [b 1 1] [b b b] [b b b] [b 2 b] [b 2 2])))
(is (= (for [x ['a nil] y [x 'b]] [x y])
'([a a] [a b] [nil nil] [nil b]))))
(deftest-both Destructuring
(is (= (for [{:syms [a b c]} (map #(zipmap '(a b c) (range % 5)) (range 3))
x [a b c]]
(Integer. (str a b c x)))
'(120 121 122 1231 1232 1233 2342 2343 2344))))
(deftest-both Let
(is (= (for [x (range 3) y (range 3) :let [z (+ x y)] :when (odd? z)] [x y z])
'([0 1 1] [1 0 1] [1 2 3] [2 1 3])))
(is (= (for [x (range 6) :let [y (rem x 2)] :when (even? y) z [8 9]] [x z])
'([0 8] [0 9] [2 8] [2 9] [4 8] [4 9]))))
; :while must skip all subsequent chunks as well as the remainder of
; the current chunk:
(deftest-both Chunked-While
(is (= (for [x (range 100) :while (even? x)] x) '(0))))
================================================
FILE: test/clojure/test_clojure/genclass/examples.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Test classes that are AOT-compile for the tests in
clojure.test-clojure.genclass."
:author "Stuart Halloway, Daniel Solano Gómez"}
clojure.test-clojure.genclass.examples)
(definterface ExampleInterface
(foo [a])
(foo [a b])
(foo [a #^int b]))
(gen-class :name clojure.test_clojure.genclass.examples.ExampleClass
:implements [clojure.test_clojure.genclass.examples.ExampleInterface])
;; -foo-Object unimplemented to test missing fn case
(defn -foo-Object-Object
[_ o1 o2]
"foo with o, o")
(defn -foo-Object-int
[_ o i]
"foo with o, i")
(declare -toString)
(gen-class :name ^{Deprecated {}
;SuppressWarnings ["Warning1"] ; discarded
;java.lang.annotation.Target []
}
clojure.test_clojure.genclass.examples.ExampleAnnotationClass
:prefix "annot-"
:methods [[;^{Deprecated {}
; Override {}} ;discarded
foo [;^{java.lang.annotation.Retention java.lang.annotation.RetentionPolicy/SOURCE
; java.lang.annotation.Target [java.lang.annotation.ElementType/TYPE
; java.lang.annotation.ElementType/PARAMETER]}
String] void]])
(comment (gen-class :name clojure.test_clojure.genclass.examples.ProtectedFinalTester
:extends java.lang.ClassLoader
:main false
:prefix "pf-"
; Cannot do this with java sources
;:exposes-methods {findSystemClass superFindSystemClass}
))
(defn pf-findSystemClass
"This function should never be called."
[_ name]
clojure.lang.RT)
(definterface ArrayDefInterface
; primitive array sugar
(^void takesByteArray [^bytes a])
(^void takesCharArray [^chars a])
(^void takesShortArray [^shorts a])
(^void takesIntArray [^ints a])
(^void takesLongArray [^longs a])
(^void takesFloatArray [^floats a])
(^void takesDoubleArray [^doubles a])
(^void takesBooleanArray [^booleans a])
; raw primitive arrays
(^"[B" returnsByteArray [])
(^"[C" returnsCharArray [])
(^"[I" returnsIntArray [])
(^"[S" returnsShortArray [])
(^"[J" returnsLongArray [])
(^"[F" returnsFloatArray [])
(^"[D" returnsDoubleArray [])
(^"[Z" returnsBooleanArray []))
(definterface UsesPreviousInterfaceFromThisFile
(^clojure.test-clojure.genclass.examples.ArrayDefInterface
identity
[^clojure.test-clojure.genclass.examples.ArrayDefInterface a]))
(gen-interface
:name clojure.test_clojure.genclass.examples.ArrayGenInterface
:methods [; sugar
[takesByteArray [bytes] void]
[takesCharArray [chars] void]
[takesShortArray [shorts] void]
[takesIntArray [ints] void]
[takesLongArray [longs] void]
[takesFloatArray [floats] void]
[takesDoubleArray [doubles] void]
[takesBooleanArray [booleans] void]
; raw primitive types
[returnsByteArray [] "[B"]
[returnsCharArray [] "[C"]
[returnsShortArray [] "[S"]
[returnsIntArray [] "[I"]
[returnsLongArray [] "[J"]
[returnsFloatArray [] "[F"]
[returnsDoubleArray [] "[D"]
[returnsBooleanArray [] "[Z"]])
================================================
FILE: test/clojure/test_clojure/genclass.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Tests for clojure.core/gen-class"
:author "Stuart Halloway, Daniel Solano Gómez"}
clojure.test-clojure.genclass
(:use clojure.test clojure.test-helper)
(:require clojure.test_clojure.genclass.examples)
(:import [clojure.test_clojure.genclass.examples
ExampleClass
ExampleAnnotationClass
ArrayDefInterface
ArrayGenInterface]
[java.lang.annotation ElementType
Retention
RetentionPolicy
Target]))
(deftest arg-support
(let [example (ExampleClass.)
o (Object.)]
(is (= "foo with o, o" (.foo example o o)))
(is (= "foo with o, i" (.foo example o (int 1))))
(is (thrown? java.lang.UnsupportedOperationException (.foo example o)))))
(deftest name-munging
(testing "mapping from Java fields to Clojure vars"
(is (= #'clojure.test-clojure.genclass.examples/-foo-Object-int
(get-field ExampleClass 'foo_Object_int__var)))
(is (= #'clojure.test-clojure.genclass.examples/-toString
(get-field ExampleClass 'toString__var)))))
;todo - fix this, it depends on the order of things out of a hash-map
#_(deftest test-annotations
(let [annot-class ExampleAnnotationClass
foo-method (.getDeclaredMethod annot-class "foo" (into-array [String]))]
(testing "Class annotations:"
(is (= 2 (count (.getDeclaredAnnotations annot-class))))
(testing "@Deprecated"
(let [deprecated (.getAnnotation annot-class Deprecated)]
(is deprecated)))
(testing "@Target([])"
(let [resource (.getAnnotation annot-class Target)]
(is (= 0 (count (.value resource)))))))
(testing "Method annotations:"
(testing "@Deprecated void foo(String):"
(is (= 1 (count (.getDeclaredAnnotations foo-method))))
(is (.getAnnotation foo-method Deprecated))))
(testing "Parameter annotations:"
(let [param-annots (.getParameterAnnotations foo-method)]
(is (= 1 (alength param-annots)))
(let [first-param-annots (aget param-annots 0)]
(is (= 2 (alength first-param-annots)))
(testing "void foo(@Retention(…) String)"
(let [retention (aget first-param-annots 0)]
(is (instance? Retention retention))
(= RetentionPolicy/SOURCE (.value retention))))
(testing "void foo(@Target(…) String)"
(let [target (aget first-param-annots 1)]
(is (instance? Target target))
(is (= [ElementType/TYPE ElementType/PARAMETER] (seq (.value target)))))))))))
(deftest genclass-option-validation
(is (fails-with-cause? IllegalArgumentException #"Not a valid method name: has-hyphen"
(@#'clojure.core/validate-generate-class-options {:methods '[[fine [] void] [has-hyphen [] void]]}))))
(deftest interface-array-type-hints
(let [array-types {:ints (class (int-array 0))
:bytes (class (byte-array 0))
:shorts (class (short-array 0))
:chars (class (char-array 0))
:longs (class (long-array 0))
:floats (class (float-array 0))
:doubles (class (double-array 0))
:booleans (class (boolean-array 0))
:maps (class (into-array java.util.Map []))}
array-types (assoc array-types
:maps-2d (class (into-array (:maps array-types) [])))
method-with-name (fn [name methods] (first (filter #(= name (.getName %)) methods)))
parameter-type (fn [method] (first (.getParameterTypes method)))
return-type (fn [method] (.getReturnType method))]
(testing "definterface"
(let [method-with-name #(method-with-name % (.getMethods ArrayDefInterface))]
(testing "sugar primitive array hints"
(are [name type] (= (type array-types)
(parameter-type (method-with-name name)))
"takesByteArray" :bytes
"takesCharArray" :chars
"takesShortArray" :shorts
"takesIntArray" :ints
"takesLongArray" :longs
"takesFloatArray" :floats
"takesDoubleArray" :doubles
"takesBooleanArray" :booleans))
(testing "raw primitive array hints"
(are [name type] (= (type array-types)
(return-type (method-with-name name)))
"returnsByteArray" :bytes
"returnsCharArray" :chars
"returnsShortArray" :shorts
"returnsIntArray" :ints
"returnsLongArray" :longs
"returnsFloatArray" :floats
"returnsDoubleArray" :doubles
"returnsBooleanArray" :booleans))))
(testing "gen-interface"
(let [method-with-name #(method-with-name % (.getMethods ArrayGenInterface))]
(testing "sugar primitive array hints"
(are [name type] (= (type array-types)
(parameter-type (method-with-name name)))
"takesByteArray" :bytes
"takesCharArray" :chars
"takesShortArray" :shorts
"takesIntArray" :ints
"takesLongArray" :longs
"takesFloatArray" :floats
"takesDoubleArray" :doubles
"takesBooleanArray" :booleans))
(testing "raw primitive array hints"
(are [name type] (= (type array-types)
(return-type (method-with-name name)))
"returnsByteArray" :bytes
"returnsCharArray" :chars
"returnsShortArray" :shorts
"returnsIntArray" :ints
"returnsLongArray" :longs
"returnsFloatArray" :floats
"returnsDoubleArray" :doubles
"returnsBooleanArray" :booleans))))))
================================================
FILE: test/clojure/test_clojure/generators.clj
================================================
(ns clojure.test-clojure.generators
(:require [clojure.data.generators :as gen])
(:refer-clojure :exclude [namespace]))
(defn var-value-source
"Generates a scalar suitable for an initial var value."
[]
(let [v (gen/scalar)]
(if (symbol? v)
`(quote ~v)
v)))
(defn var-source
[n]
`(def ~(symbol (str "var" n))
~(var-value-source)))
(defn record-source
[n]
(let [rname (str "ExampleRecord" "-" n)
fldct (gen/geometric 0.1)]
`(defrecord ~(symbol rname) ~(vec (map #(symbol (str "f" %)) (range fldct))))))
(defn generate-namespaces
"Returns a map with :nses, :vars, :records"
[{:keys [nses vars-per-ns records-per-ns]}]
(let [nses (mapv #(create-ns (symbol (str "clojure.generated.ns" %)))
(range nses))
_ (doseq [ns nses] (binding [*ns* ns] (refer 'clojure.core)))
make-in-ns (fn [ns src] (binding [*ns* ns] (eval src)))
vars (->> (mapcat
(fn [ns]
(map
#(make-in-ns ns (var-source %))
(range vars-per-ns)))
nses)
(into []))
records (->> (mapcat
(fn [ns]
(map
#(make-in-ns ns (record-source %))
(range records-per-ns)))
nses)
(into []))]
{:nses nses
:vars vars
:records records}))
(def shared-generation
(delay (generate-namespaces {:nses 5 :vars-per-ns 5 :records-per-ns 5})))
(defn namespace
[]
(gen/rand-nth (:nses @shared-generation)))
(defn var
[]
(gen/rand-nth (:vars @shared-generation)))
(defn record
[]
(gen/rand-nth (:records @shared-generation)))
(def keyword-pool
(delay
(binding [gen/*rnd* (java.util.Random. 42)]
(into [] (repeatedly 1000 gen/keyword)))))
(defn keyword-from-pool
[]
(gen/rand-nth @keyword-pool))
(def symbol-pool
(delay
(binding [gen/*rnd* (java.util.Random. 42)]
(into [] (repeatedly 1000 gen/symbol)))))
(defn symbol-from-pool
[]
(gen/rand-nth @keyword-pool))
(def ednable-scalars
[(constantly nil)
gen/byte
gen/long
gen/boolean
gen/printable-ascii-char
gen/string
symbol-from-pool
keyword-from-pool
gen/uuid
gen/date
gen/ratio
gen/bigint
gen/bigdec])
(defn- call-through
"Recursively call x until it doesn't return a function."
[x]
(if (fn? x)
(recur (x))
x))
(defn ednable-scalar
[]
(call-through (rand-nth ednable-scalars)))
(def ednable-collections
[[gen/vec [ednable-scalars]]
[gen/set [ednable-scalars]]
[gen/hash-map [ednable-scalars ednable-scalars]]])
(defn ednable-collection
[]
(let [[coll args] (rand-nth ednable-collections)]
(apply coll (map rand-nth args))))
(defn ednable
[]
(gen/one-of ednable-scalar ednable-collection))
(defn non-ednable
"Generate something that can be printed with *print-dup*, but
cannot be read back via edn/read."
[]
(gen/one-of namespace var))
(defn dup-readable
"Generate something that requires print-dup to be printed in
a roundtrippable way."
[]
(gen/one-of namespace var))
================================================
FILE: test/clojure/test_clojure/java/io.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.java.io
(:use clojure.test clojure.java.io
[clojure.test-helper :only [platform-newlines]])
(:import (java.io File BufferedInputStream
FileInputStream InputStreamReader InputStream
FileOutputStream OutputStreamWriter OutputStream
ByteArrayInputStream ByteArrayOutputStream)
(java.net URL URI Socket ServerSocket)))
(defn temp-file
[prefix suffix]
(doto (File/createTempFile prefix suffix)
(.deleteOnExit)))
;; does not work on IBM JDK
#_(deftest test-spit-and-slurp
(let [f (temp-file "clojure.java.io" "test")
content (apply str (concat "a" (repeat 500 "\u226a\ud83d\ude03")))]
(spit f content)
(is (= content (slurp f)))
;; UTF-16 must be last for the following test
(doseq [enc [ "UTF-8" "UTF-16BE" "UTF-16LE" "UTF-16" ]]
(spit f content :encoding enc)
(is (= content (slurp f :encoding enc))))
(testing "deprecated arity"
(is (=
(platform-newlines "WARNING: (slurp f enc) is deprecated, use (slurp f :encoding enc).\n")
(with-out-str
(is (= content (slurp f "UTF-16")))))))))
(deftest test-streams-defaults
(let [f (temp-file "clojure.java.io" "test-reader-writer")
content "testing"]
(try
(is (thrown? Exception (reader (Object.))))
(is (thrown? Exception (writer (Object.))))
(are [write-to read-from] (= content (do
(spit write-to content :encoding "UTF-8")
(slurp read-from :encoding "UTF-8")))
f f
(.getAbsolutePath f) (.getAbsolutePath f)
(.toURL f) (.toURL f)
(.toURI f) (.toURI f)
(FileOutputStream. f) (FileInputStream. f)
(OutputStreamWriter. (FileOutputStream. f) "UTF-8") (reader f :encoding "UTF-8")
f (FileInputStream. f)
(writer f :encoding "UTF-8") (InputStreamReader. (FileInputStream. f) "UTF-8"))
(is (= content (slurp (.getBytes content "UTF-8"))))
(is (= content (slurp (.toCharArray content))))
(finally
(.delete f)))))
(defn bytes-should-equal [byte-array-1 byte-array-2 msg]
(is (= @#'clojure.java.io/byte-array-type (class byte-array-1) (class byte-array-2)) msg)
(is (= (into [] byte-array-1) (into [] byte-array-2)) msg))
(defn data-fixture
"in memory fixture data for tests"
[encoding]
(let [s (apply str (concat "a" (repeat 500 "\u226a\ud83d\ude03")))
bs (.getBytes s encoding)
cs (.toCharArray s)
i (ByteArrayInputStream. bs)
;; Make UTF-8 encoding explicit for the InputStreamReader and
;; OutputStreamWriter, since some JVMs use a different default
;; encoding.
r (InputStreamReader. i "UTF-8")
o (ByteArrayOutputStream.)
w (OutputStreamWriter. o "UTF-8")]
{:bs bs
:i i
:r r
:o o
:s s
:cs cs
:w w}))
(deftest test-copy
(dorun
(for [{:keys [in out flush] :as test}
[{:in :i :out :o}
{:in :i :out :w}
{:in :r :out :o}
{:in :r :out :w}
{:in :cs :out :o}
{:in :cs :out :w}
{:in :bs :out :o}
{:in :bs :out :w}]
opts
[{} {:buffer-size 16} {:buffer-size 256}]]
(let [{:keys [s o] :as d} (data-fixture "UTF-8")]
(apply copy (in d) (out d) (flatten (vec opts)))
#_(when (= out :w) (.flush (:w d)))
(.flush (out d))
(bytes-should-equal (.getBytes s "UTF-8")
(.toByteArray o)
(str "combination " test opts))))))
;; does not work on IBM JDK
#_(deftest test-copy-encodings
(doseq [enc [ "UTF-8" "UTF-16" "UTF-16BE" "UTF-16LE" ]]
(testing (str "from inputstream " enc " to writer UTF-8")
(let [{:keys [i s o w bs]} (data-fixture enc)]
(copy i w :encoding enc :buffer-size 16)
(.flush w)
(bytes-should-equal (.getBytes s "UTF-8") (.toByteArray o) "")))
(testing (str "from reader UTF-8 to output-stream " enc)
(let [{:keys [r o s]} (data-fixture "UTF-8")]
(copy r o :encoding enc :buffer-size 16)
(bytes-should-equal (.getBytes s enc) (.toByteArray o) "")))))
(deftest test-as-file
(are [result input] (= result (as-file input))
(File. "foo") "foo"
(File. "bar") (File. "bar")
(File. "baz") (URL. "file:baz")
(File. "bar+baz") (URL. "file:bar+baz")
(File. "bar baz qux") (URL. "file:bar%20baz%20qux")
(File. "quux") (URI. "file:quux")
(File. "abcíd/foo.txt") (URL. "file:abc%c3%add/foo.txt")
nil nil))
(deftest test-resources-with-spaces
(let [file-with-spaces (temp-file "test resource 2" "txt")
url (as-url (.getParentFile file-with-spaces))
loader (java.net.URLClassLoader. (into-array [url]))
r (resource (.getName file-with-spaces) loader)]
(is (= r (as-url file-with-spaces)))
(spit r "foobar")
(is (= "foobar" (slurp r)))))
(deftest test-file
(are [result args] (= (File. result) (apply file args))
"foo" ["foo"]
"foo/bar" ["foo" "bar"]
"foo/bar/baz" ["foo" "bar" "baz"]))
(deftest test-as-url
(are [file-part input] (= (URL. (str "file:" file-part)) (as-url input))
"foo" "file:foo"
"baz" (URL. "file:baz")
"quux" (URI. "file:quux"))
(is (nil? (as-url nil))))
(deftest test-delete-file
(let [file (temp-file "test" "deletion")
not-file (File. (str (java.util.UUID/randomUUID)))]
(delete-file (.getAbsolutePath file))
(is (not (.exists file)))
(is (thrown? java.io.IOException (delete-file not-file)))
(is (= :silently (delete-file not-file :silently)))))
(deftest test-as-relative-path
(testing "strings"
(is (= "foo" (as-relative-path "foo"))))
(testing "absolute path strings are forbidden"
(is (thrown? IllegalArgumentException (as-relative-path (.getAbsolutePath (File. "baz"))))))
(testing "relative File paths"
(is (= "bar" (as-relative-path (File. "bar")))))
(testing "absolute File paths are forbidden"
(is (thrown? IllegalArgumentException (as-relative-path (File. (.getAbsolutePath (File. "quux"))))))))
(defn stream-should-have [stream expected-bytes msg]
(let [actual-bytes (byte-array (alength expected-bytes))]
(.read stream actual-bytes)
(is (= -1 (.read stream)) (str msg " : should be end of stream"))
(is (= (seq expected-bytes) (seq actual-bytes)) (str msg " : byte arrays should match"))))
(deftest test-input-stream
(let [file (temp-file "test-input-stream" "txt")
content (apply str (concat "a" (repeat 500 "\u226a\ud83d\ude03")))
bytes (.getBytes content "UTF-8")]
(spit file content)
(doseq [[expr msg]
[[file File]
[(FileInputStream. file) FileInputStream]
[(BufferedInputStream. (FileInputStream. file)) BufferedInputStream]
[(.. file toURI) URI]
[(.. file toURI toURL) URL]
[(.. file toURI toURL toString) "URL as String"]
[(.. file toString) "File as String"]]]
(with-open [s (input-stream expr)]
(stream-should-have s bytes msg)))))
(deftest test-streams-buffering
(let [data (.getBytes "")]
(is (instance? java.io.BufferedReader (reader data)))
(is (instance? java.io.BufferedWriter (writer (java.io.ByteArrayOutputStream.))))
(is (instance? java.io.BufferedInputStream (input-stream data)))
(is (instance? java.io.BufferedOutputStream (output-stream (java.io.ByteArrayOutputStream.))))))
(deftest test-resource
(is (nil? (resource "non/existent/resource")))
(is (instance? URL (resource "clojure/core.clj")))
(let [file (temp-file "test-resource" "txt")
url (as-url (.getParentFile file))
loader (java.net.URLClassLoader. (into-array [url]))]
(is (nil? (resource "non/existent/resource" loader)))
(is (instance? URL (resource (.getName file) loader)))))
(deftest test-make-parents
(let [tmp (System/getProperty "java.io.tmpdir")]
(delete-file (file tmp "test-make-parents" "child" "grandchild") :silently)
(delete-file (file tmp "test-make-parents" "child") :silently)
(delete-file (file tmp "test-make-parents") :silently)
(make-parents tmp "test-make-parents" "child" "grandchild")
(is (.isDirectory (file tmp "test-make-parents" "child")))
(is (not (.isDirectory (file tmp "test-make-parents" "child" "grandchild"))))
(delete-file (file tmp "test-make-parents" "child"))
(delete-file (file tmp "test-make-parents"))))
(deftest test-socket-iofactory
(let [server-socket (ServerSocket. 0)
client-socket (Socket. "localhost" (.getLocalPort server-socket))]
(try
(is (instance? InputStream (input-stream client-socket)))
(is (instance? OutputStream (output-stream client-socket)))
(finally (.close server-socket)
(.close client-socket)))))
================================================
FILE: test/clojure/test_clojure/java/javadoc.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.java.javadoc
(:use clojure.test
[clojure.java.javadoc :as j])
(:import (java.io File)))
(deftest javadoc-url-test
(testing "for a core api"
(binding [*feeling-lucky* false]
(are [x y] (= x (#'j/javadoc-url y))
nil "foo.Bar"
(str *core-java-api* "java/lang/String.html") "java.lang.String")))
(testing "for a remote javadoc"
(binding [*remote-javadocs* (ref (sorted-map "java." "http://example.com/"))]
(is (= "http://example.com/java/lang/Number.html" (#'j/javadoc-url "java.lang.Number"))))))
================================================
FILE: test/clojure/test_clojure/java/shell.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.java.shell
(:use clojure.test
[clojure.java.shell :as sh])
(:import (java.io File)))
(def platform-enc (.name (java.nio.charset.Charset/defaultCharset)))
(def default-enc "UTF-8")
(deftest test-parse-args
(are [x y] (= x y)
[[] {:in-enc default-enc :out-enc default-enc :dir nil :env nil}] (#'sh/parse-args [])
[["ls"] {:in-enc default-enc :out-enc default-enc :dir nil :env nil}] (#'sh/parse-args ["ls"])
[["ls" "-l"] {:in-enc default-enc :out-enc default-enc :dir nil :env nil}] (#'sh/parse-args ["ls" "-l"])
[["ls"] {:in-enc default-enc :out-enc "ISO-8859-1" :dir nil :env nil}] (#'sh/parse-args ["ls" :out-enc "ISO-8859-1"])
[[] {:in-enc platform-enc :out-enc platform-enc :dir nil :env nil}] (#'sh/parse-args [:in-enc platform-enc :out-enc platform-enc])))
(deftest test-with-sh-dir
(are [x y] (= x y)
nil *sh-dir*
"foo" (with-sh-dir "foo" *sh-dir*)))
(deftest test-with-sh-env
(are [x y] (= x y)
nil *sh-env*
{:KEY "VAL"} (with-sh-env {:KEY "VAL"} *sh-env*)))
(deftest test-as-env-strings
(are [x y] (= x y)
nil (#'sh/as-env-strings nil)
["FOO=BAR"] (seq (#'sh/as-env-strings {"FOO" "BAR"}))
["FOO_SYMBOL=BAR"] (seq (#'sh/as-env-strings {'FOO_SYMBOL "BAR"}))
["FOO_KEYWORD=BAR"] (seq (#'sh/as-env-strings {:FOO_KEYWORD "BAR"}))))
================================================
FILE: test/clojure/test_clojure/java_interop.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.java-interop
(:use clojure.test))
; http://clojure.org/java_interop
; http://clojure.org/compilation
(deftest test-dot
; (.instanceMember instance args*)
(are [x] (= x "FRED")
(.toUpperCase "fred")
(. "fred" toUpperCase)
(. "fred" (toUpperCase)) )
(are [x] (= x true)
(.startsWith "abcde" "ab")
(. "abcde" startsWith "ab")
(. "abcde" (startsWith "ab")) )
; (.instanceMember Classname args*)
(are [x] (= x "java.lang.String")
(.getName String)
(. (identity String) getName)
(. (identity String) (getName)) )
; (Classname/staticMethod args*)
(are [x] (= x 7)
(Math/abs -7)
(. Math abs -7)
(. Math (abs -7)) )
; (. target -prop)
(let [p (java.awt.Point. 1 2)]
(are [x y] (= x y)
1 (.-x p)
2 (.-y p)
1 (. p -x)
2 (. p -y)
1 (. (java.awt.Point. 1 2) -x)
2 (. (java.awt.Point. 1 2) -y)))
; Classname/staticField
(are [x] (= x 2147483647)
Integer/MAX_VALUE
(. Integer MAX_VALUE) ))
(definterface I (a []))
(deftype T [a] I (a [_] "method"))
(deftest test-reflective-field-name-ambiguous
(let [t (->T "field")]
(is (= "method" (. ^T t a)))
(is (= "field" (. ^T t -a)))
(is (= "method" (. t a)))
(is (= "field" (. t -a)))
(is (thrown? IllegalArgumentException (. t -BOGUS)))))
(deftest test-double-dot
(is (= (.. System (getProperties) (get "os.name"))
(. (. System (getProperties)) (get "os.name")))))
(deftest test-doto
(let [m (doto (new java.util.HashMap)
(.put "a" 1)
(.put "b" 2))]
(are [x y] (= x y)
(class m) java.util.HashMap
m {"a" 1 "b" 2} )))
(deftest test-new
; Integer
(are [expr cls value] (and (= (class expr) cls)
(= expr value))
(new java.lang.Integer 42) java.lang.Integer 42
(java.lang.Integer. 123) java.lang.Integer 123 )
; Date
(are [x] (= (class x) java.util.Date)
(new java.util.Date)
(java.util.Date.) ))
(deftest test-instance?
; evaluation
(are [x y] (= x y)
(instance? java.lang.Integer (+ 1 2)) false
(instance? java.lang.Long (+ 1 2)) true )
; different types
(are [type literal] (instance? literal type)
1 java.lang.Long
1.0 java.lang.Double
1M java.math.BigDecimal
\a java.lang.Character
"a" java.lang.String )
; it is a Long, nothing else
(are [x y] (= (instance? x 42) y)
java.lang.Integer false
java.lang.Long true
java.lang.Character false
java.lang.String false )
; test compiler macro
(is (let [Long String] (instance? Long "abc")))
(is (thrown? clojure.lang.ArityException (instance? Long))))
; set!
; memfn
(comment
(deftest test-bean
(let [b (bean java.awt.Color/black)]
(are [x y] (= x y)
(map? b) true
(:red b) 0
(:green b) 0
(:blue b) 0
(:RGB b) -16777216
(:alpha b) 255
(:transparency b) 1
(:missing b) nil
(:missing b :default) :default
(get b :missing) nil
(get b :missing :default) :default
(:class b) java.awt.Color ))))
(comment
(deftest test-iterable-bean
(is (.iterator ^Iterable (bean (java.util.Date.))))
(is (hash (bean (java.util.Date.))))))
; proxy, proxy-super
(deftest test-proxy-chain
(testing "That the proxy functions can chain"
(are [x y] (= x y)
(-> (get-proxy-class Object)
construct-proxy
(init-proxy {})
(update-proxy {"toString" (fn [_] "chain chain chain")})
str)
"chain chain chain"
(-> (proxy [Object] [] (toString [] "superfuzz bigmuff"))
(update-proxy {"toString" (fn [_] "chain chain chain")})
str)
"chain chain chain")))
(deftest test-bases
(are [x y] (= x y)
(bases java.lang.Math)
(list java.lang.Object)
(bases java.util.Collection)
(list java.lang.Iterable)
(bases java.lang.Object)
nil
(bases java.lang.Comparable)
nil
(bases java.lang.Integer)
(list java.lang.Number java.lang.Comparable) ))
(deftest test-supers
(are [x y] (= x y)
(supers java.lang.Math)
#{java.lang.Object}
(supers java.lang.Integer)
#{java.lang.Number java.lang.Object
java.lang.Comparable java.io.Serializable} ))
(deftest test-proxy-super
(let [d (proxy [java.util.BitSet] []
(flip [bitIndex]
(try
(proxy-super flip bitIndex)
(catch IndexOutOfBoundsException e
(throw (IllegalArgumentException. "replaced"))))))]
;; normal call
(is (nil? (.flip d 0)))
;; exception should use proxied form and return IllegalArg
(is (thrown? IllegalArgumentException (.flip d -1)))
;; same behavior on second call
(is (thrown? IllegalArgumentException (.flip d -1)))))
; Arrays: [alength] aget aset [make-array to-array into-array to-array-2d aclone]
; [float-array, int-array, etc]
; amap, areduce
(defmacro deftest-type-array [type-array type]
`(deftest ~(symbol (str "test-" type-array))
; correct type
#_(is (= (class (first (~type-array [1 2]))) (class (~type 1))))
; given size (and empty)
(are [x] (and (= (alength (~type-array x)) x)
(= (vec (~type-array x)) (repeat x 0)))
0 1 5 )
; copy of a sequence
(are [x] (and (= (alength (~type-array x)) (count x))
(= (vec (~type-array x)) x))
[]
[1]
[1 -2 3 0 5] )
; given size and init-value
(are [x] (and (= (alength (~type-array x 42)) x)
(= (vec (~type-array x 42)) (repeat x 42)))
0 1 5 )
; given size and init-seq
(are [x y z] (and (= (alength (~type-array x y)) x)
(= (vec (~type-array x y)) z))
0 [] []
0 [1] []
0 [1 2 3] []
1 [] [0]
1 [1] [1]
1 [1 2 3] [1]
5 [] [0 0 0 0 0]
5 [1] [1 0 0 0 0]
5 [1 2 3] [1 2 3 0 0]
5 [1 2 3 4 5] [1 2 3 4 5]
5 [1 2 3 4 5 6 7] [1 2 3 4 5] )))
(deftest-type-array int-array int)
(deftest-type-array long-array long)
;todo, fix, test broken for float/double, should compare to 1.0 2.0 etc
#_(deftest-type-array float-array float)
#_(deftest-type-array double-array double)
; separate test for exceptions (doesn't work with above macro...)
(deftest test-type-array-exceptions
(are [x] (thrown? NegativeArraySizeException x)
(int-array -1)
(long-array -1)
(float-array -1)
(double-array -1) ))
(deftest test-make-array
; negative size
(is (thrown? NegativeArraySizeException (make-array Integer -1)))
; one-dimensional
(are [x] (= (alength (make-array Integer x)) x)
0 1 5 )
(let [a (make-array Long 5)]
(aset a 3 42)
(are [x y] (= x y)
(aget a 3) 42
(class (aget a 3)) Long ))
; multi-dimensional
(let [a (make-array Long 3 2 4)]
(aset a 0 1 2 987)
(are [x y] (= x y)
(alength a) 3
(alength (first a)) 2
(alength (first (first a))) 4
(aget a 0 1 2) 987
(class (aget a 0 1 2)) Long )))
(deftest test-to-array
(let [v [1 "abc" :kw \c []]
a (to-array v)]
(are [x y] (= x y)
; length
(alength a) (count v)
; content
(vec a) v
(class (aget a 0)) (class (nth v 0))
(class (aget a 1)) (class (nth v 1))
(class (aget a 2)) (class (nth v 2))
(class (aget a 3)) (class (nth v 3))
(class (aget a 4)) (class (nth v 4)) ))
; different kinds of collections
(are [x] (and (= (alength (to-array x)) (count x))
(= (vec (to-array x)) (vec x)))
()
'(1 2)
[]
[1 2]
(sorted-set)
(sorted-set 1 2)
(int-array 0)
(int-array [1 2 3])
(to-array [])
(to-array [1 2 3]) ))
(defn queue [& contents]
(apply conj (clojure.lang.PersistentQueue/EMPTY) contents))
(defn array-typed-equals [expected actual]
(and (= (class expected) (class actual))
(java.util.Arrays/equals expected actual)))
(defmacro test-to-passed-array-for [collection-type]
`(deftest ~(symbol (str "test-to-passed-array-for-" collection-type))
(let [string-array# (make-array String 5)
shorter# (~collection-type "1" "2" "3")
same-length# (~collection-type "1" "2" "3" "4" "5")
longer# (~collection-type "1" "2" "3" "4" "5" "6")]
(are [expected actual] (array-typed-equals expected actual)
(into-array String ["1" "2" "3" nil nil]) (.toArray shorter# string-array#)
(into-array String ["1" "2" "3" "4" "5"]) (.toArray same-length# string-array#)
(into-array String ["1" "2" "3" "4" "5" "6"]) (.toArray longer# string-array#)))))
(test-to-passed-array-for vector)
(test-to-passed-array-for list)
;;(test-to-passed-array-for hash-set)
(test-to-passed-array-for queue)
(deftest test-into-array
; compatible types only
(is (thrown? IllegalArgumentException (into-array [1 "abc" :kw])))
(is (thrown? IllegalArgumentException (into-array [1.2 4])))
(is (thrown? IllegalArgumentException (into-array [(byte 2) (short 3)])))
(is (thrown? IllegalArgumentException (into-array Byte/TYPE [100000000000000])))
; simple case
(let [v [1 2 3 4 5]
a (into-array v)]
(are [x y] (= x y)
(alength a) (count v)
(vec a) v
(class (first a)) (class (first v)) ))
(is (= \a (aget (into-array Character/TYPE [\a \b \c]) 0)))
(let [types [Integer/TYPE
Byte/TYPE
Float/TYPE
Short/TYPE
Double/TYPE
Long/TYPE]
values [(byte 2) (short 3) (int 4) 5]]
(for [t types]
(let [a (into-array t values)]
(is (== (aget a 0) 2))
(is (== (aget a 1) 3))
(is (== (aget a 2) 4))
(is (== (aget a 3) 5)))))
; different kinds of collections
(are [x] (and (= (alength (into-array x)) (count x))
(= (vec (into-array x)) (vec x))
(= (alength (into-array Long/TYPE x)) (count x))
(= (vec (into-array Long/TYPE x)) (vec x)))
()
'(1 2)
[]
[1 2]
(sorted-set)
(sorted-set 1 2)
(int-array 0)
(int-array [1 2 3])
(to-array [])
(to-array [1 2 3]) ))
(deftest test-to-array-2d
; needs to be a collection of collection(s)
(is (thrown? Exception (to-array-2d [1 2 3])))
; ragged array
(let [v [[1] [2 3] [4 5 6]]
a (to-array-2d v)]
(are [x y] (= x y)
(alength a) (count v)
(alength (aget a 0)) (count (nth v 0))
(alength (aget a 1)) (count (nth v 1))
(alength (aget a 2)) (count (nth v 2))
(vec (aget a 0)) (nth v 0)
(vec (aget a 1)) (nth v 1)
(vec (aget a 2)) (nth v 2) ))
; empty array
(let [a (to-array-2d [])]
(are [x y] (= x y)
(alength a) 0
(vec a) [] )))
(deftest test-alength
(are [x] (= (alength x) 0)
(int-array 0)
(long-array 0)
(float-array 0)
(double-array 0)
(boolean-array 0)
(byte-array 0)
(char-array 0)
(short-array 0)
(make-array Integer/TYPE 0)
(to-array [])
(into-array [])
(to-array-2d []) )
(are [x] (= (alength x) 1)
(int-array 1)
(long-array 1)
(float-array 1)
(double-array 1)
(boolean-array 1)
(byte-array 1)
(char-array 1)
(short-array 1)
(make-array Integer/TYPE 1)
(to-array [1])
(into-array [1])
(to-array-2d [[1]]) )
(are [x] (= (alength x) 3)
(int-array 3)
(long-array 3)
(float-array 3)
(double-array 3)
(boolean-array 3)
(byte-array 3)
(char-array 3)
(short-array 3)
(make-array Integer/TYPE 3)
(to-array [1 "a" :k])
(into-array [1 2 3])
(to-array-2d [[1] [2 3] [4 5 6]]) ))
(deftest test-aclone
; clone all arrays except 2D
(are [x] (and (= (alength (aclone x)) (alength x))
(= (vec (aclone x)) (vec x)))
(int-array 0)
(long-array 0)
(float-array 0)
(double-array 0)
(boolean-array 0)
(byte-array 0)
(char-array 0)
(short-array 0)
(make-array Integer/TYPE 0)
(to-array [])
(into-array [])
(int-array [1 2 3])
(long-array [1 2 3])
(float-array [1 2 3])
(double-array [1 2 3])
(boolean-array [true false])
(byte-array [(byte 1) (byte 2)])
(byte-array [1 2])
(byte-array 2 [1 2])
(char-array [\a \b \c])
(short-array [(short 1) (short 2)])
(short-array [1 2])
(short-array 2 [1 2])
(make-array Integer/TYPE 3)
(to-array [1 "a" :k])
(into-array [1 2 3]) )
; clone 2D
(are [x] (and (= (alength (aclone x)) (alength x))
(= (map alength (aclone x)) (map alength x))
(= (map vec (aclone x)) (map vec x)))
(to-array-2d [])
(to-array-2d [[1] [2 3] [4 5 6]]) ))
; Type Hints, *warn-on-reflection*
; #^ints, #^floats, #^longs, #^doubles
; Coercions: [int, long, float, double, char, boolean, short, byte]
; num
; ints/longs/floats/doubles
(deftest test-boolean
(are [x y] (and (instance? java.lang.Boolean (boolean x))
(= (boolean x) y))
nil false
false false
true true
0 true
1 true
() true
[1] true
"" true
\space true
:kw true ))
(deftest test-char
; int -> char
(is (instance? java.lang.Character (char 65)))
; char -> char
(is (instance? java.lang.Character (char \a)))
(is (= (char \a) \a)))
;; Note: More coercions in numbers.clj
================================================
FILE: test/clojure/test_clojure/keywords.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test-clojure.keywords
(:use clojure.test))
(let [this-ns (str (.name *ns*))]
(deftest test-find-keyword
:foo
::foo
(let [absent-keyword-sym (gensym "absent-keyword-sym")]
(are [result lookup] (= result (find-keyword lookup))
:foo :foo
:foo 'foo
:foo "foo"
nil absent-keyword-sym
nil (str absent-keyword-sym))
(are [result lookup] (= result (find-keyword this-ns lookup))
::foo "foo"
nil (str absent-keyword-sym)))))
================================================
FILE: test/clojure/test_clojure/logic.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
;;
;; Created 1/29/2009
(ns clojure.test-clojure.logic
(:use clojure.test
[clojure.test-helper :only (exception)]))
;; *** Tests ***
(deftest test-if
; true/false/nil
(are [x y] (= x y)
(if true :t) :t
(if true :t :f) :t
(if true :t (exception)) :t
(if false :t) nil
(if false :t :f) :f
(if false (exception) :f) :f
(if nil :t) nil
(if nil :t :f) :f
(if nil (exception) :f) :f )
; zero/empty is true
(are [x] (= (if x :t :f) :t)
(byte 0)
(short 0)
(int 0)
(long 0)
(bigint 0)
(float 0)
(double 0)
(bigdec 0)
0/2
""
#""
(symbol "")
()
[]
{}
#{}
(into-array []) )
; anything except nil/false is true
(are [x] (= (if x :t :f) :t)
(byte 2)
(short 2)
(int 2)
(long 2)
(bigint 2)
(float 2)
(double 2)
(bigdec 2)
2/3
\a
"abc"
#"a*b"
'abc
:kw
'(1 2)
[1 2]
{:a 1 :b 2}
#{1 2}
(into-array [1 2])
(new java.util.Date) ))
(deftest test-nil-punning
(are [x y] (= (if x :no :yes) y)
(first []) :yes
(next [1]) :yes
(rest [1]) :no
(butlast [1]) :yes
(seq nil) :yes
(seq []) :yes
(sequence nil) :no
(sequence []) :no
(lazy-seq nil) :no
(lazy-seq []) :no
(filter #(> % 10) [1 2 3]) :no
(map identity []) :no
(apply concat []) :no
(concat) :no
(concat []) :no
(reverse nil) :no
(reverse []) :no
(sort nil) :no
(sort []) :no ))
(deftest test-and
(are [x y] (= x y)
(and) true
(and true) true
(and nil) nil
(and false) false
(and true nil) nil
(and true false) false
(and 1 true :kw 'abc "abc") "abc"
(and 1 true :kw nil 'abc "abc") nil
(and 1 true :kw nil (exception) 'abc "abc") nil
(and 1 true :kw 'abc "abc" false) false
(and 1 true :kw 'abc "abc" false (exception)) false ))
(deftest test-or
(are [x y] (= x y)
(or) nil
(or true) true
(or nil) nil
(or false) false
(or nil false true) true
(or nil false 1 2) 1
(or nil false "abc" :kw) "abc"
(or false nil) nil
(or nil false) false
(or nil nil nil false) false
(or nil true false) true
(or nil true (exception) false) true
(or nil false "abc" (exception)) "abc" ))
(deftest test-not
; (is (thrown? IllegalArgumentException (not)))
(are [x] (= (not x) true)
nil
false )
(are [x] (= (not x) false)
true
; numbers
0
0.0
42
1.2
0/2
2/3
; characters
\space
\tab
\a
; strings
""
"abc"
; regexes
#""
#"a*b"
; symbols
(symbol "")
'abc
; keywords
:kw
; collections/arrays
()
'(1 2)
[]
[1 2]
{}
{:a 1 :b 2}
#{}
#{1 2}
(into-array [])
(into-array [1 2])
; Java objects
(new java.util.Date) ))
(deftest test-some?
(are [expected x] (= expected (some? x))
false nil
true false
true 0
true "abc"
true []))
================================================
FILE: test/clojure/test_clojure/macros.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.macros
(:use clojure.test))
; http://clojure.org/macros
; ->
; defmacro definline macroexpand-1 macroexpand
;; -> and ->> should not be dependent on the meaning of their arguments
(defmacro c
[arg]
(if (= 'b (first arg))
:foo
:bar))
(deftest ->test
(let [a 2, b identity]
(is (= (-> a b c)
(c (b a))))))
(deftest ->>test
(let [a 2, b identity]
(is (= (->> a b c)
(c (b a))))))
(deftest ->metadata-test
(testing "a trivial form"
(is (= {:hardy :har :har :-D}
(meta (macroexpand-1 (list `-> (with-meta
'quoted-symbol
{:hardy :har :har :-D})))))))
(testing "a nontrivial form"
(let [a (with-meta 'a {:foo :bar})
b (with-meta '(b c d) {:bar :baz})
e (with-meta 'e {:baz :quux})
expanded (macroexpand-1 (list `-> a b e))]
(is (= expanded '(e (b a c d))))
(is (= {:baz :quux} (meta (first expanded))))
(is (= {:bar :baz} (meta (second expanded))))
(is (= {:foo :bar} (meta (second (second expanded))))))))
(deftest ->>metadata-test
(testing "a trivial form"
(is (= {:hardy :har :har :-D}
(meta (macroexpand-1 (list `->> (with-meta
'quoted-symbol
{:hardy :har :har :-D})))))))
(testing "a non-trivial form"
(let [a (with-meta 'a {:foo :bar})
b (with-meta '(b c d) {:bar :baz})
e (with-meta 'e {:baz :quux})
expanded (macroexpand-1 (list `->> a b e))]
(is (= expanded '(e (b c d a))))
(is (= {:baz :quux} (meta (first expanded))))
(is (= {:bar :baz} (meta (second expanded))))
(is (= {:foo :bar} (meta (last (second expanded))))))))
================================================
FILE: test/clojure/test_clojure/main.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Stuart Halloway
(ns clojure.test-clojure.main
(:use clojure.test
[clojure.test-helper :only [platform-newlines]])
(:require [clojure.main :as main]))
(deftest eval-opt
(testing "evals and prints forms"
(is (= (platform-newlines "2\n4\n") (with-out-str (#'clojure.main/eval-opt "(+ 1 1) (+ 2 2)")))))
(testing "skips printing nils"
(is (= (platform-newlines ":a\n:c\n") (with-out-str (#'clojure.main/eval-opt ":a nil :c")))))
(testing "does not block access to *in* (#299)"
(with-in-str "(+ 1 1)"
(is (= (platform-newlines "(+ 1 1)\n") (with-out-str (#'clojure.main/eval-opt "(read)")))))))
(defmacro with-err-str
"Evaluates exprs in a context in which *err* is bound to a fresh
StringWriter. Returns the string created by any nested printing
calls."
[& body]
`(let [s# (new java.io.StringWriter)
p# (new java.io.PrintWriter s#)]
(binding [*err* p#]
~@body
(str s#))))
(defn run-repl-and-return-err
"Run repl, swallowing stdout and returing stderr."
[in-str]
(with-err-str
(with-out-str
(with-in-str in-str
(main/repl)))))
;argh - test fragility, please fix
#_(deftest repl-exception-safety
(testing "catches and prints exception on bad equals"
(is (re-matches #"java\.lang\.NullPointerException\r?\n"
(run-repl-and-return-err
"(proxy [Object] [] (equals [o] (.toString nil)))")))))
================================================
FILE: test/clojure/test_clojure/metadata.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Authors: Stuart Halloway, Frantisek Sodomka
(ns clojure.test-clojure.metadata
(:use clojure.test
[clojure.test-helper :only (eval-in-temp-ns)])
(:require [clojure.set :as set]))
(def public-namespaces
'[clojure.core
clojure.pprint
;clojure.inspector
clojure.set
clojure.stacktrace
clojure.test
clojure.walk
clojure.xml
clojure.zip
clojure.java.io
;clojure.java.browse
;clojure.java.javadoc
;clojure.java.shell
clojure.string
clojure.data])
(doseq [ns public-namespaces]
(require ns))
(def public-vars
(mapcat #(vals (ns-publics %)) public-namespaces))
(def public-vars-with-docstrings
(filter (comp :doc meta) public-vars))
(def public-vars-with-docstrings-not-generated
(remove #(re-find #"^->[A-Z]" (name (.sym %))) public-vars-with-docstrings))
(deftest public-vars-with-docstrings-have-added
(is (= [] (remove (comp :added meta) public-vars-with-docstrings-not-generated))))
(deftest interaction-of-def-with-metadata
(testing "initial def sets metadata"
(let [v (eval-in-temp-ns
(def ^{:a 1} foo 0)
#'foo)]
(is (= 1 (-> v meta :a)))))
#_(testing "subsequent declare doesn't overwrite metadata"
(let [v (eval-in-temp-ns
(def ^{:b 2} bar 0)
(declare bar)
#'bar)]
(is (= 2 (-> v meta :b))))
(testing "when compiled"
(let [v (eval-in-temp-ns
(def ^{:c 3} bar 0)
(defn declare-bar []
(declare bar))
(declare-bar)
#'bar)]
(is (= 3 (-> v meta :c))))))
(testing "subsequent def with init-expr *does* overwrite metadata"
(let [v (eval-in-temp-ns
(def ^{:d 4} quux 0)
(def quux 1)
#'quux)]
(is (nil? (-> v meta :d))))
(testing "when compiled"
(let [v (eval-in-temp-ns
(def ^{:e 5} quux 0)
(defn def-quux []
(def quux 1))
(def-quux)
#'quux)]
(is (nil? (-> v meta :e))))))
(testing "IllegalArgumentException should not be thrown"
(testing "when defining var whose value is calculated with a primitive fn."
(testing "This case fails without a fix for CLJ-852"
(is (eval-in-temp-ns
(defn foo ^long [^long x] x)
(def x (inc (foo 10))))))
(testing "This case should pass even without a fix for CLJ-852"
(is (eval-in-temp-ns
(defn foo ^long [^long x] x)
(def x (foo (inc 10)))))))))
(deftest fns-preserve-metadata-on-maps
(let [xm {:a 1 :b -7}
x (with-meta {:foo 1 :bar 2} xm)
ym {:c "foo"}
y (with-meta {:baz 4 :guh x} ym)]
(is (= xm (meta (:guh y))))
(is (= xm (meta (reduce #(assoc %1 %2 (inc %2)) x (range 1000)))))
(is (= xm (meta (-> x (dissoc :foo) (dissoc :bar)))))
(let [z (assoc-in y [:guh :la] 18)]
(is (= ym (meta z)))
(is (= xm (meta (:guh z)))))
(let [z (update-in y [:guh :bar] inc)]
(is (= ym (meta z)))
(is (= xm (meta (:guh z)))))
(is (= xm (meta (get-in y [:guh]))))
(is (= xm (meta (into x y))))
(is (= ym (meta (into y x))))
(is (= xm (meta (merge x y))))
(is (= ym (meta (merge y x))))
(is (= xm (meta (merge-with + x y))))
(is (= ym (meta (merge-with + y x))))
(is (= xm (meta (select-keys x [:bar]))))
(is (= xm (meta (set/rename-keys x {:foo :new-foo}))))
;; replace returns a seq when given a set. Can seqs have
;; metadata?
;; TBD: rseq, subseq, and rsubseq returns seqs. If it is even
;; possible to put metadata on a seq, does it make sense that the
;; seqs returned by these functions should have the same metadata
;; as the sorted collection on which they are called?
))
(deftest fns-preserve-metadata-on-vectors
(let [xm {:a 1 :b -7}
x (with-meta [1 2 3] xm)
ym {:c "foo"}
y (with-meta [4 x 6] ym)]
(is (= xm (meta (y 1))))
(is (= xm (meta (assoc x 1 "one"))))
(is (= xm (meta (reduce #(conj %1 %2) x (range 1000)))))
(is (= xm (meta (pop (pop (pop x))))))
(let [z (assoc-in y [1 2] 18)]
(is (= ym (meta z)))
(is (= xm (meta (z 1)))))
(let [z (update-in y [1 2] inc)]
(is (= ym (meta z)))
(is (= xm (meta (z 1)))))
(is (= xm (meta (get-in y [1]))))
(is (= xm (meta (into x y))))
(is (= ym (meta (into y x))))
(is (= xm (meta (replace {2 "two"} x))))
(is (= [1 "two" 3] (replace {2 "two"} x)))
;; TBD: Currently subvec drops metadata. Should it preserve it?
;;(is (= xm (meta (subvec x 2 3))))
;; TBD: rseq returns a seq. If it is even possible to put
;; metadata on a seq, does it make sense that the seqs returned by
;; these functions should have the same metadata as the sorted
;; collection on which they are called?
))
(deftest fns-preserve-metadata-on-sets
;; TBD: Do tests independently for set, hash-set, and sorted-set,
;; perhaps with a loop here.
(let [xm {:a 1 :b -7}
x (with-meta #{1 2 3} xm)
ym {:c "foo"}
y (with-meta #{4 x 6} ym)]
(is (= xm (meta (y #{3 2 1}))))
(is (= xm (meta (reduce #(conj %1 %2) x (range 1000)))))
(is (= xm (meta (-> x (disj 1) (disj 2) (disj 3)))))
(is (= xm (meta (into x y))))
(is (= ym (meta (into y x))))
(is (= xm (meta (set/select even? x))))
(let [cow1m {:what "betsy cow"}
cow1 (with-meta {:name "betsy" :id 33} cow1m)
cow2m {:what "panda cow"}
cow2 (with-meta {:name "panda" :id 34} cow2m)
cowsm {:what "all the cows"}
cows (with-meta #{cow1 cow2} cowsm)
cow-names (set/project cows [:name])
renamed (set/rename cows {:id :number})]
(is (= cowsm (meta cow-names)))
(is (= cow1m (meta (first (filter #(= "betsy" (:name %)) cow-names)))))
(is (= cow2m (meta (first (filter #(= "panda" (:name %)) cow-names)))))
(is (= cowsm (meta renamed)))
(is (= cow1m (meta (first (filter #(= "betsy" (:name %)) renamed)))))
(is (= cow2m (meta (first (filter #(= "panda" (:name %)) renamed))))))
;; replace returns a seq when given a set. Can seqs have
;; metadata?
;; union: Currently returns the metadata of the largest input set.
;; This is an artifact of union's current implementation. I doubt
;; any explicit design decision was made to do so. Like join,
;; there doesn't seem to be much reason to prefer the metadata of
;; one input set over another, if at least two input sets are
;; given, but perhaps defining it to always return a set with the
;; metadata of the first input set would be reasonable?
;; intersection: Returns metadata of the smallest input set.
;; Otherwise similar to union.
;; difference: Seems to always return a set with metadata of first
;; input set. Seems reasonable. Not sure we want to add a test
;; for it, if it is an accident of the current implementation.
;; join, index, map-invert: Currently always returns a value with
;; no metadata. This seems reasonable.
))
(deftest defn-primitive-args
(testing "Hinting the arg vector of a primitive-taking fn with a non-primitive type should not result in AbstractMethodError when invoked."
(testing "CLJ-850 is fixed when this case passes."
(is (= "foo"
(eval-in-temp-ns
(defn f ^String [^String s ^long i] s)
(f "foo" 1)))))
(testing "These cases should pass, even without a fix for CLJ-850."
(is (= "foo"
(eval-in-temp-ns
(defn f ^String [^String s] s)
(f "foo"))))
(is (= 1
(eval-in-temp-ns
(defn f ^long [^String s ^long i] i)
(f "foo" 1))))
(is (= 1
(eval-in-temp-ns
(defn f ^long [^long i] i)
(f 1)))))))
================================================
FILE: test/clojure/test_clojure/multimethods.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka, Robert Lachlan
(ns clojure.test-clojure.multimethods
(:use clojure.test [clojure.test-helper :only (with-var-roots)])
(:require [clojure.set :as set]))
; http://clojure.org/multimethods
; defmulti
; defmethod
; remove-method
; prefer-method
; methods
; prefers
(defmacro for-all
[& args]
`(dorun (for ~@args)))
(defn hierarchy-tags
"Return all tags in a derivation hierarchy"
[h]
(set/select
#(instance? clojure.lang.Named %)
(reduce into #{} (map keys (vals h)))))
(defn transitive-closure
"Return all objects reachable by calling f starting with o,
not including o itself. f should return a collection."
[o f]
(loop [results #{}
more #{o}]
(let [new-objects (set/difference more results)]
(if (seq new-objects)
(recur (set/union results more) (reduce into #{} (map f new-objects)))
(disj results o)))))
(defn tag-descendants
"Set of descedants which are tags (i.e. Named)."
[& args]
(set/select
#(instance? clojure.lang.Named %)
(or (apply descendants args) #{})))
(defn assert-valid-hierarchy
[h]
(let [tags (hierarchy-tags h)]
(testing "ancestors are the transitive closure of parents"
(for-all [tag tags]
(is (= (transitive-closure tag #(parents h %))
(or (ancestors h tag) #{})))))
(testing "ancestors are transitive"
(for-all [tag tags]
(is (= (transitive-closure tag #(ancestors h %))
(or (ancestors h tag) #{})))))
(testing "tag descendants are transitive"
(for-all [tag tags]
(is (= (transitive-closure tag #(tag-descendants h %))
(or (tag-descendants h tag) #{})))))
(testing "a tag isa? all of its parents"
(for-all [tag tags
:let [parents (parents h tag)]
parent parents]
(is (isa? h tag parent))))
(testing "a tag isa? all of its ancestors"
(for-all [tag tags
:let [ancestors (ancestors h tag)]
ancestor ancestors]
(is (isa? h tag ancestor))))
(testing "all my descendants have me as an ancestor"
(for-all [tag tags
:let [descendants (descendants h tag)]
descendant descendants]
(is (isa? h descendant tag))))
(testing "there are no cycles in parents"
(for-all [tag tags]
(is (not (contains? (transitive-closure tag #(parents h %)) tag)))))
(testing "there are no cycles in descendants"
(for-all [tag tags]
(is (not (contains? (descendants h tag) tag)))))))
(def family
(reduce #(apply derive (cons %1 %2)) (make-hierarchy)
[[::parent-1 ::ancestor-1]
[::parent-1 ::ancestor-2]
[::parent-2 ::ancestor-2]
[::child ::parent-2]
[::child ::parent-1]]))
(deftest cycles-are-forbidden
(testing "a tag cannot be its own parent"
(is (thrown-with-msg? Throwable #"\(not= tag parent\)"
(derive family ::child ::child))))
(testing "a tag cannot be its own ancestor"
(is (thrown-with-msg? Throwable #"Cyclic derivation: :clojure.test-clojure.multimethods/child has :clojure.test-clojure.multimethods/ancestor-1 as ancestor"
(derive family ::ancestor-1 ::child)))))
(deftest using-diamond-inheritance
(let [diamond (reduce #(apply derive (cons %1 %2)) (make-hierarchy)
[[::mammal ::animal]
[::bird ::animal]
[::griffin ::mammal]
[::griffin ::bird]])
bird-no-more (underive diamond ::griffin ::bird)]
(assert-valid-hierarchy diamond)
(assert-valid-hierarchy bird-no-more)
(testing "a griffin is a mammal, indirectly through mammal and bird"
(is (isa? diamond ::griffin ::animal)))
(testing "a griffin is a bird"
(is (isa? diamond ::griffin ::bird)))
(testing "after underive, griffin is no longer a bird"
(is (not (isa? bird-no-more ::griffin ::bird))))
(testing "but it is still an animal, via mammal"
(is (isa? bird-no-more ::griffin ::animal)))))
(deftest derivation-world-bridges-to-java-inheritance
(let [h (derive (make-hierarchy) java.util.Map ::map)]
(testing "a Java class can be isa? a tag"
(is (isa? h java.util.Map ::map)))
(testing "if a Java class isa? a tag, so are its subclasses..."
(is (isa? h java.util.HashMap ::map)))
(testing "...but not its superclasses!"
(is (not (isa? h java.util.Collection ::map))))))
(deftest global-hierarchy-test
(with-var-roots {#'clojure.core/global-hierarchy (make-hierarchy)}
(assert-valid-hierarchy @#'clojure.core/global-hierarchy)
(testing "when you add some derivations..."
(derive ::lion ::cat)
(derive ::manx ::cat)
(assert-valid-hierarchy @#'clojure.core/global-hierarchy))
(testing "...isa? sees the derivations"
(is (isa? ::lion ::cat))
(is (not (isa? ::cat ::lion))))
(testing "... you can traverse the derivations"
(is (= #{::manx ::lion} (descendants ::cat)))
(is (= #{::cat} (parents ::manx)))
(is (= #{::cat} (ancestors ::manx))))
(testing "then, remove a derivation..."
(underive ::manx ::cat))
(testing "... traversals update accordingly"
(is (= #{::lion} (descendants ::cat)))
(is (nil? (parents ::manx)))
(is (nil? (ancestors ::manx))))))
#_(defmacro for-all
"Better than the actual for-all, if only it worked."
[& args]
`(reduce
#(and %1 %2)
(map true? (for ~@args))))
(deftest basic-multimethod-test
(testing "Check basic dispatch"
(defmulti too-simple identity)
(defmethod too-simple :a [x] :a)
(defmethod too-simple :b [x] :b)
(defmethod too-simple :default [x] :default)
(is (= :a (too-simple :a)))
(is (= :b (too-simple :b)))
(is (= :default (too-simple :c))))
(testing "Remove a method works"
(remove-method too-simple :a)
(is (= :default (too-simple :a))))
(testing "Add another method works"
(defmethod too-simple :d [x] :d)
(is (= :d (too-simple :d)))))
(deftest isA-multimethod-test
(testing "Dispatch on isA"
;; Example from the multimethod docs.
(derive java.util.Map ::collection)
(derive java.util.Collection ::collection)
(defmulti foo class)
(defmethod foo ::collection [c] :a-collection)
(defmethod foo String [s] :a-string)
(is (= :a-collection (foo [])))
(is (= :a-collection (foo (java.util.HashMap.))))
(is (= :a-string (foo "bar")))))
(deftest preferences-multimethod-test
(testing "Multiple method match dispatch error is caught"
;; Example from the multimethod docs.
(derive ::rect ::shape)
(defmulti bar (fn [x y] [x y]))
(defmethod bar [::rect ::shape] [x y] :rect-shape)
(defmethod bar [::shape ::rect] [x y] :shape-rect)
(is (thrown? java.lang.IllegalArgumentException
(bar ::rect ::rect))))
(testing "The prefers method returns empty table w/ no prefs"
(= {} (prefers bar)))
(testing "Adding a preference to resolve it dispatches correctly"
(prefer-method bar [::rect ::shape] [::shape ::rect])
(is (= :rect-shape (bar ::rect ::rect))))
(testing "The prefers method now returns the correct table"
(is (= {[::rect ::shape] #{[::shape ::rect]}} (prefers bar)))))
(deftest remove-all-methods-test
(testing "Core function remove-all-methods works"
(defmulti simple1 identity)
(defmethod simple1 :a [x] :a)
(defmethod simple1 :b [x] :b)
(is (= {} (methods (remove-all-methods simple1))))))
(deftest methods-test
(testing "Core function methods works"
(defmulti simple2 identity)
(defmethod simple2 :a [x] :a)
(defmethod simple2 :b [x] :b)
(is (= #{:a :b} (into #{} (keys (methods simple2)))))
(is (= :a ((:a (methods simple2)) 1)))
(defmethod simple2 :c [x] :c)
(is (= #{:a :b :c} (into #{} (keys (methods simple2)))))
(remove-method simple2 :a)
(is (= #{:b :c} (into #{} (keys (methods simple2)))))))
(deftest get-method-test
(testing "Core function get-method works"
(defmulti simple3 identity)
(defmethod simple3 :a [x] :a)
(defmethod simple3 :b [x] :b)
(is (fn? (get-method simple3 :a)))
(is (= (:a ((get-method simple3 :a) 1))))
(is (fn? (get-method simple3 :b)))
(is (= (:b ((get-method simple3 :b) 1))))
(is (nil? (get-method simple3 :c)))))
================================================
FILE: test/clojure/test_clojure/ns_libs.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Authors: Frantisek Sodomka, Stuart Halloway
(ns clojure.test-clojure.ns-libs
(:use clojure.test))
; http://clojure.org/namespaces
; in-ns ns create-ns
; alias import intern refer
; all-ns find-ns
; ns-name ns-aliases ns-imports ns-interns ns-map ns-publics ns-refers
; resolve ns-resolve namespace
; ns-unalias ns-unmap remove-ns
; http://clojure.org/libs
; require use
; loaded-libs
(deftest test-alias
(is (thrown-with-msg? Exception #"No namespace: epicfail found" (alias 'bogus 'epicfail))))
(deftest test-require
(is (thrown? Exception (require :foo)))
(is (thrown? Exception (require))))
(deftest test-use
(is (thrown? Exception (use :foo)))
(is (thrown? Exception (use))))
(deftest reimporting-deftypes
(let [inst1 (binding [*ns* *ns*]
(eval '(do (ns exporter)
(defrecord ReimportMe [a])
(ns importer)
(import exporter.ReimportMe)
(ReimportMe. 1))))
inst2 (binding [*ns* *ns*]
(eval '(do (ns exporter)
(defrecord ReimportMe [a b])
(ns importer)
(import exporter.ReimportMe)
(ReimportMe. 1 2))))]
(testing "you can reimport a changed class and see the changes"
(is (= [:a] (keys inst1)))
(is (= [:a :b] (keys inst2))))
;fragile tests, please fix
#_(testing "you cannot import same local name from a different namespace"
(is (thrown? clojure.lang.Compiler$CompilerException
#"ReimportMe already refers to: class exporter.ReimportMe in namespace: importer"
(binding [*ns* *ns*]
(eval '(do (ns exporter-2)
(defrecord ReimportMe [a b])
(ns importer)
(import exporter-2.ReimportMe)
(ReimportMe. 1 2)))))))))
(deftest naming-types
(testing "you cannot use a name already referred from another namespace"
(is (thrown? IllegalStateException
#"String already refers to: class java.lang.String"
(definterface String)))
(is (thrown? IllegalStateException
#"StringBuffer already refers to: class java.lang.StringBuffer"
(deftype StringBuffer [])))
(is (thrown? IllegalStateException
#"Integer already refers to: class java.lang.Integer"
(defrecord Integer [])))))
(deftest resolution
(let [s (gensym)]
(are [result expr] (= result expr)
#'clojure.core/first (ns-resolve 'clojure.core 'first)
nil (ns-resolve 'clojure.core s)
nil (ns-resolve 'clojure.core {'first :local-first} 'first)
nil (ns-resolve 'clojure.core {'first :local-first} s))))
(deftest refer-error-messages
(let [temp-ns (gensym)]
(binding [*ns* *ns*]
(in-ns temp-ns)
(eval '(def ^{:private true} hidden-var)))
#_(testing "referring to something that does not exist"
(is (thrown-with-msg? IllegalAccessError #"nonexistent-var does not exist"
(refer temp-ns :only '(nonexistent-var)))))
#_(testing "referring to something non-public"
(is (thrown-with-msg? IllegalAccessError #"hidden-var is not public"
(refer temp-ns :only '(hidden-var)))))))
(deftest test-defrecord-deftype-err-msg
(is (thrown-with-msg? clojure.lang.Compiler$CompilerException
#"defrecord and deftype fields must be symbols, user\.MyRecord had: :shutdown-fn, compiling:"
(eval '(defrecord MyRecord [:shutdown-fn]))))
(is (thrown-with-msg? clojure.lang.Compiler$CompilerException
#"defrecord and deftype fields must be symbols, user\.MyType had: :key1, compiling:"
(eval '(deftype MyType [:key1])))))
================================================
FILE: test/clojure/test_clojure/numbers.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Stephen C. Gilardi
;; scgilardi (gmail)
;; Created 30 October 2008
;;
(ns clojure.test-clojure.numbers
(:use clojure.test
[clojure.test.generative :exclude (is)]
clojure.template)
(:require [clojure.data.generators :as gen]
[clojure.test-helper :as helper]))
; TODO:
; ==
; and more...
;; *** Types ***
(deftest Coerced-BigDecimal
(doseq [v [(bigdec 3) (bigdec (inc (bigint Long/MAX_VALUE)))]]
(are [x] (true? x)
(instance? BigDecimal v)
(number? v)
(decimal? v)
(not (float? v)))))
(deftest BigInteger-conversions
(doseq [coerce-fn [bigint biginteger]]
(doseq [v (map coerce-fn [ Long/MAX_VALUE
13178456923875639284562345789M
13178456923875639284562345789N
Float/MAX_VALUE
(- Float/MAX_VALUE)
Double/MAX_VALUE
(- Double/MAX_VALUE)
(* 2 (bigdec Double/MAX_VALUE)) ])]
(are [x] (true? x)
(integer? v)
(number? v)
(not (decimal? v))
(not (float? v))))))
(defn all-pairs-equal [equal-var vals]
(doseq [val1 vals]
(doseq [val2 vals]
(is (equal-var val1 val2)
(str "Test that " val1 " (" (class val1) ") "
equal-var " " val2 " (" (class val2) ")")))))
(defn all-pairs-hash-consistent-with-= [vals]
(doseq [val1 vals]
(doseq [val2 vals]
(when (= val1 val2)
(is (= (hash val1) (hash val2))
(str "Test that (hash " val1 ") (" (class val1) ") "
" = (hash " val2 ") (" (class val2) ")"))))))
(deftest equality-tests
;; = only returns true for numbers that are in the same category,
;; where category is one of INTEGER, FLOATING, DECIMAL, RATIO.
(all-pairs-equal #'= [(byte 2) (short 2) (int 2) (long 2)
(bigint 2) (biginteger 2)])
(all-pairs-equal #'= [(float 2.0) (double 2.0)])
(all-pairs-equal #'= [2.0M 2.00M])
(all-pairs-equal #'= [(float 1.5) (double 1.5)])
(all-pairs-equal #'= [1.50M 1.500M])
(all-pairs-equal #'= [0.0M 0.00M])
(all-pairs-equal #'= [(/ 1 2) (/ 2 4)])
;; No BigIntegers or floats in following tests, because hash
;; consistency with = for them is out of scope for Clojure
;; (CLJ-1036).
(all-pairs-hash-consistent-with-= [(byte 2) (short 2) (int 2) (long 2)
(bigint 2)
(double 2.0) 2.0M 2.00M])
(all-pairs-hash-consistent-with-= [(/ 3 2) (double 1.5) 1.50M 1.500M])
(all-pairs-hash-consistent-with-= [(double 0.0) 0.0M 0.00M])
;; == tests for numerical equality, returning true even for numbers
;; in different categories.
(all-pairs-equal #'== [(byte 0) (short 0) (int 0) (long 0)
(bigint 0) (biginteger 0)
(float 0.0) (double 0.0) 0.0M 0.00M])
(all-pairs-equal #'== [(byte 2) (short 2) (int 2) (long 2)
(bigint 2) (biginteger 2)
(float 2.0) (double 2.0) 2.0M 2.00M])
(all-pairs-equal #'== [(/ 3 2) (float 1.5) (double 1.5) 1.50M 1.500M]))
(deftest unchecked-cast-num-obj
(do-template [prim-array cast]
(are [n]
(let [a (prim-array 1)]
(aset a 0 (cast n)))
(Byte. Byte/MAX_VALUE)
(Short. Short/MAX_VALUE)
(Integer. Integer/MAX_VALUE)
(Long. Long/MAX_VALUE)
(Float. Float/MAX_VALUE)
(Double. Double/MAX_VALUE))
byte-array
unchecked-byte
short-array
unchecked-short
char-array
unchecked-char
int-array
unchecked-int
long-array
unchecked-long
float-array
unchecked-float
double-array
unchecked-double))
(deftest unchecked-cast-num-prim
(do-template [prim-array cast]
(are [n]
(let [a (prim-array 1)]
(aset a 0 (cast n)))
Byte/MAX_VALUE
Short/MAX_VALUE
Integer/MAX_VALUE
Long/MAX_VALUE
Float/MAX_VALUE
Double/MAX_VALUE)
byte-array
unchecked-byte
short-array
unchecked-short
char-array
unchecked-char
int-array
unchecked-int
long-array
unchecked-long
float-array
unchecked-float
double-array
unchecked-double))
(deftest unchecked-cast-char
; in keeping with the checked cast functions, char and Character can only be cast to int
(is (unchecked-int (char 0xFFFF)))
(is (let [c (char 0xFFFF)] (unchecked-int c)))) ; force primitive char
(def expected-casts
[
[:input [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE Long/MAX_VALUE Float/MAX_VALUE Double/MAX_VALUE]]
[char [:error (char 0) (char 1) (char 127) (char 32767) :error :error :error :error]]
[unchecked-char [(char 65535) (char 0) (char 1) (char 127) (char 32767) (char 65535) (char 65535) (char 65535) (char 65535)]]
[byte [-1 0 1 Byte/MAX_VALUE :error :error :error :error :error]]
[unchecked-byte [-1 0 1 Byte/MAX_VALUE -1 -1 -1 -1 -1]]
[short [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE :error :error :error :error]]
[unchecked-short [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE -1 -1 -1 -1]]
[int [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE :error :error :error]]
[unchecked-int [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE -1 Integer/MAX_VALUE Integer/MAX_VALUE]]
[long [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE Long/MAX_VALUE :error :error]]
[unchecked-long [-1 0 1 Byte/MAX_VALUE Short/MAX_VALUE Integer/MAX_VALUE Long/MAX_VALUE Long/MAX_VALUE Long/MAX_VALUE]]
;; 2.14748365E9 if when float/double conversion is avoided...
[float [-1.0 0.0 1.0 127.0 32767.0 2.147483648E9 9.223372036854776E18 Float/MAX_VALUE :error]]
[unchecked-float [-1.0 0.0 1.0 127.0 32767.0 2.147483648E9 9.223372036854776E18 Float/MAX_VALUE Float/POSITIVE_INFINITY]]
[double [-1.0 0.0 1.0 127.0 32767.0 2.147483647E9 9.223372036854776E18 Float/MAX_VALUE Double/MAX_VALUE]]
[unchecked-double [-1.0 0.0 1.0 127.0 32767.0 2.147483647E9 9.223372036854776E18 Float/MAX_VALUE Double/MAX_VALUE]]])
(deftest test-expected-casts
(let [[[_ inputs] & expectations] expected-casts]
(doseq [[f vals] expectations]
(let [wrapped (fn [x]
(try
(f x)
(catch IllegalArgumentException e :error)))]
(is (= vals (map wrapped inputs)))))))
;; *** Functions ***
(defonce DELTA 1e-12)
(deftest test-add
(are [x y] (= x y)
(+) 0
(+ 1) 1
(+ 1 2) 3
(+ 1 2 3) 6
(+ -1) -1
(+ -1 -2) -3
(+ -1 +2 -3) -2
(+ 1 -1) 0
(+ -1 1) 0
(+ 2/3) 2/3
(+ 2/3 1) 5/3
(+ 2/3 1/3) 1 )
(are [x y] (< (- x y) DELTA)
(+ 1.2) 1.2
(+ 1.1 2.4) 3.5
(+ 1.1 2.2 3.3) 6.6 )
(is (> (+ Integer/MAX_VALUE 10) Integer/MAX_VALUE)) ; no overflow
(is (thrown? ClassCastException (+ "ab" "cd"))) ) ; no string concatenation
(deftest test-subtract
(is (thrown? IllegalArgumentException (-)))
(are [x y] (= x y)
(- 1) -1
(- 1 2) -1
(- 1 2 3) -4
(- -2) 2
(- 1 -2) 3
(- 1 -2 -3) 6
(- 1 1) 0
(- -1 -1) 0
(- 2/3) -2/3
(- 2/3 1) -1/3
(- 2/3 1/3) 1/3 )
(are [x y] (< (- x y) DELTA)
(- 1.2) -1.2
(- 2.2 1.1) 1.1
(- 6.6 2.2 1.1) 3.3 )
(is (< (- Integer/MIN_VALUE 10) Integer/MIN_VALUE)) ) ; no underflow
(deftest test-multiply
(are [x y] (= x y)
(*) 1
(* 2) 2
(* 2 3) 6
(* 2 3 4) 24
(* -2) -2
(* 2 -3) -6
(* 2 -3 -1) 6
(* 1/2) 1/2
(* 1/2 1/3) 1/6
(* 1/2 1/3 -1/4) -1/24 )
(are [x y] (< (- x y) DELTA)
(* 1.2) 1.2
(* 2.0 1.2) 2.4
(* 3.5 2.0 1.2) 8.4 )
(is (> (* 3 (int (/ Integer/MAX_VALUE 2.0))) Integer/MAX_VALUE)) ) ; no overflow
(deftest test-multiply-longs-at-edge
(are [x] (= x 9223372036854775808N)
(*' -1 Long/MIN_VALUE)
(*' Long/MIN_VALUE -1)
(* -1N Long/MIN_VALUE)
(* Long/MIN_VALUE -1N)
(* -1 (bigint Long/MIN_VALUE))
(* (bigint Long/MIN_VALUE) -1))
(is (thrown? ArithmeticException (* Long/MIN_VALUE -1)))
(is (thrown? ArithmeticException (* -1 Long/MIN_VALUE))))
(deftest test-ratios-simplify-to-ints-where-appropriate
(testing "negative denominator (assembla #275)"
(is (integer? (/ 1 -1/2)))
(is (integer? (/ 0 -1/2)))))
(deftest test-divide
(are [x y] (= x y)
(/ 1) 1
(/ 2) 1/2
(/ 3 2) 3/2
(/ 4 2) 2
(/ 24 3 2) 4
(/ 24 3 2 -1) -4
(/ -1) -1
(/ -2) -1/2
(/ -3 -2) 3/2
(/ -4 -2) 2
(/ -4 2) -2 )
(are [x y] (< (- x y) DELTA)
(/ 4.5 3) 1.5
(/ 4.5 3.0 3.0) 0.5 )
(is (thrown? ArithmeticException (/ 0)))
(is (thrown? ArithmeticException (/ 2 0)))
(is (thrown? IllegalArgumentException (/))) )
;; mod
;; http://en.wikipedia.org/wiki/Modulo_operation
;; http://mathforum.org/library/drmath/view/52343.html
;;
;; is mod correct?
;; http://groups.google.com/group/clojure/browse_frm/thread/2a0ee4d248f3d131#
;;
;; Issue 23: mod (modulo) operator
;; http://code.google.com/p/clojure/issues/detail?id=23
(deftest test-mod
; wrong number of args
; (is (thrown? IllegalArgumentException (mod)))
; (is (thrown? IllegalArgumentException (mod 1)))
; (is (thrown? IllegalArgumentException (mod 3 2 1)))
; divide by zero
(comment
(is (thrown? ArithmeticException (mod 9 0)))
(is (thrown? ArithmeticException (mod 0 0)))
)
(are [x y] (= x y)
(mod 4 2) 0
(mod 3 2) 1
(mod 6 4) 2
(mod 0 5) 0
(mod 2 1/2) 0
(mod 2/3 1/2) 1/6
(mod 1 2/3) 1/3
(mod 4.0 2.0) 0.0
(mod 4.5 2.0) 0.5
; |num| > |div|, num != k * div
(mod 42 5) 2 ; (42 / 5) * 5 + (42 mod 5) = 8 * 5 + 2 = 42
(mod 42 -5) -3 ; (42 / -5) * (-5) + (42 mod -5) = -9 * (-5) + (-3) = 42
(mod -42 5) 3 ; (-42 / 5) * 5 + (-42 mod 5) = -9 * 5 + 3 = -42
(mod -42 -5) -2 ; (-42 / -5) * (-5) + (-42 mod -5) = 8 * (-5) + (-2) = -42
; |num| > |div|, num = k * div
(mod 9 3) 0 ; (9 / 3) * 3 + (9 mod 3) = 3 * 3 + 0 = 9
(mod 9 -3) 0
(mod -9 3) 0
(mod -9 -3) 0
; |num| < |div|
(mod 2 5) 2 ; (2 / 5) * 5 + (2 mod 5) = 0 * 5 + 2 = 2
(mod 2 -5) -3 ; (2 / -5) * (-5) + (2 mod -5) = (-1) * (-5) + (-3) = 2
(mod -2 5) 3 ; (-2 / 5) * 5 + (-2 mod 5) = (-1) * 5 + 3 = -2
(mod -2 -5) -2 ; (-2 / -5) * (-5) + (-2 mod -5) = 0 * (-5) + (-2) = -2
; num = 0, div != 0
(mod 0 3) 0 ; (0 / 3) * 3 + (0 mod 3) = 0 * 3 + 0 = 0
(mod 0 -3) 0
; large args
(mod 3216478362187432 432143214) 120355456
)
)
;; rem & quot
;; http://en.wikipedia.org/wiki/Remainder
(deftest test-rem
; wrong number of args
; (is (thrown? IllegalArgumentException (rem)))
; (is (thrown? IllegalArgumentException (rem 1)))
; (is (thrown? IllegalArgumentException (rem 3 2 1)))
; divide by zero
(comment
(is (thrown? ArithmeticException (rem 9 0)))
(is (thrown? ArithmeticException (rem 0 0)))
)
(are [x y] (= x y)
(rem 4 2) 0
(rem 3 2) 1
(rem 6 4) 2
(rem 0 5) 0
(rem 2 1/2) 0
(rem 2/3 1/2) 1/6
(rem 1 2/3) 1/3
(rem 4.0 2.0) 0.0
(rem 4.5 2.0) 0.5
; |num| > |div|, num != k * div
(rem 42 5) 2 ; (8 * 5) + 2 == 42
(rem 42 -5) 2 ; (-8 * -5) + 2 == 42
(rem -42 5) -2 ; (-8 * 5) + -2 == -42
(rem -42 -5) -2 ; (8 * -5) + -2 == -42
; |num| > |div|, num = k * div
(rem 9 3) 0
(rem 9 -3) 0
(rem -9 3) 0
(rem -9 -3) 0
; |num| < |div|
(rem 2 5) 2
(rem 2 -5) 2
(rem -2 5) -2
(rem -2 -5) -2
; num = 0, div != 0
(rem 0 3) 0
(rem 0 -3) 0
)
)
(deftest test-quot
; wrong number of args
; (is (thrown? IllegalArgumentException (quot)))
; (is (thrown? IllegalArgumentException (quot 1)))
; (is (thrown? IllegalArgumentException (quot 3 2 1)))
; divide by zero
(comment
(is (thrown? ArithmeticException (quot 9 0)))
(is (thrown? ArithmeticException (quot 0 0)))
)
(are [x y] (= x y)
(quot 4 2) 2
(quot 3 2) 1
(quot 6 4) 1
(quot 0 5) 0
(quot 2 1/2) 4
(quot 2/3 1/2) 1
(quot 1 2/3) 1
(quot 4.0 2.0) 2.0
(quot 4.5 2.0) 2.0
; |num| > |div|, num != k * div
(quot 42 5) 8 ; (8 * 5) + 2 == 42
(quot 42 -5) -8 ; (-8 * -5) + 2 == 42
(quot -42 5) -8 ; (-8 * 5) + -2 == -42
(quot -42 -5) 8 ; (8 * -5) + -2 == -42
; |num| > |div|, num = k * div
(quot 9 3) 3
(quot 9 -3) -3
(quot -9 3) -3
(quot -9 -3) 3
; |num| < |div|
(quot 2 5) 0
(quot 2 -5) 0
(quot -2 5) 0
(quot -2 -5) 0
; num = 0, div != 0
(quot 0 3) 0
(quot 0 -3) 0
)
)
;; *** Predicates ***
;; pos? zero? neg?
(deftest test-pos?-zero?-neg?
(let [nums [[(byte 2) (byte 0) (byte -2)]
[(short 3) (short 0) (short -3)]
[(int 4) (int 0) (int -4)]
[(long 5) (long 0) (long -5)]
[(bigint 6) (bigint 0) (bigint -6)]
[(float 7) (float 0) (float -7)]
[(double 8) (double 0) (double -8)]
[(bigdec 9) (bigdec 0) (bigdec -9)]
[2/3 0 -2/3]]
pred-result [[pos? [true false false]]
[zero? [false true false]]
[neg? [false false true]]] ]
(doseq [pr pred-result]
(doseq [n nums]
(is (= (map (first pr) n) (second pr))
(pr-str (first pr) n))))))
;; even? odd?
(deftest test-even?
(are [x] (true? x)
(even? -4)
(not (even? -3))
(even? 0)
(not (even? 5))
(even? 8))
(is (thrown? IllegalArgumentException (even? 1/2)))
(is (thrown? IllegalArgumentException (even? (double 10)))))
(deftest test-odd?
(are [x] (true? x)
(not (odd? -4))
(odd? -3)
(not (odd? 0))
(odd? 5)
(not (odd? 8)))
(is (thrown? IllegalArgumentException (odd? 1/2)))
(is (thrown? IllegalArgumentException (odd? (double 10)))))
(defn- expt
"clojure.contrib.math/expt is a better and much faster impl, but this works.
Math/pow overflows to Infinity."
[x n] (apply *' (replicate n x)))
(deftest test-bit-shift-left
(are [x y] (= x y)
2r10 (bit-shift-left 2r1 1)
2r100 (bit-shift-left 2r1 2)
2r1000 (bit-shift-left 2r1 3)
2r00101110 (bit-shift-left 2r00010111 1)
2r00101110 (apply bit-shift-left [2r00010111 1])
0 (bit-shift-left 2r10 -1) ; truncated to least 6-bits, 63
(expt 2 32) (bit-shift-left 1 32)
(expt 2 16) (bit-shift-left 1 10000) ; truncated to least 6-bits, 16
)
(is (thrown? IllegalArgumentException (bit-shift-left 1N 1))))
(deftest test-bit-shift-right
(are [x y] (= x y)
2r0 (bit-shift-right 2r1 1)
2r010 (bit-shift-right 2r100 1)
2r001 (bit-shift-right 2r100 2)
2r000 (bit-shift-right 2r100 3)
2r0001011 (bit-shift-right 2r00010111 1)
2r0001011 (apply bit-shift-right [2r00010111 1])
0 (bit-shift-right 2r10 -1) ; truncated to least 6-bits, 63
1 (bit-shift-right (expt 2 32) 32)
1 (bit-shift-right (expt 2 16) 10000) ; truncated to least 6-bits, 16
-1 (bit-shift-right -2r10 1)
)
(is (thrown? IllegalArgumentException (bit-shift-right 1N 1))))
(deftest test-unsigned-bit-shift-right
(are [x y] (= x y)
2r0 (unsigned-bit-shift-right 2r1 1)
2r010 (unsigned-bit-shift-right 2r100 1)
2r001 (unsigned-bit-shift-right 2r100 2)
2r000 (unsigned-bit-shift-right 2r100 3)
2r0001011 (unsigned-bit-shift-right 2r00010111 1)
2r0001011 (apply unsigned-bit-shift-right [2r00010111 1])
0 (unsigned-bit-shift-right 2r10 -1) ; truncated to least 6-bits, 63
1 (unsigned-bit-shift-right (expt 2 32) 32)
1 (unsigned-bit-shift-right (expt 2 16) 10000) ; truncated to least 6-bits, 16
9223372036854775807 (unsigned-bit-shift-right -2r10 1)
)
(is (thrown? IllegalArgumentException (unsigned-bit-shift-right 1N 1))))
(deftest test-bit-clear
(is (= 2r1101 (bit-clear 2r1111 1)))
(is (= 2r1101 (bit-clear 2r1101 1))))
(deftest test-bit-set
(is (= 2r1111 (bit-set 2r1111 1)))
(is (= 2r1111 (bit-set 2r1101 1))))
(deftest test-bit-flip
(is (= 2r1101 (bit-flip 2r1111 1)))
(is (= 2r1111 (bit-flip 2r1101 1))))
(deftest test-bit-test
(is (true? (bit-test 2r1111 1)))
(is (false? (bit-test 2r1101 1))))
;; arrays
(deftest test-array-types
(are [x y z] (= (Class/forName x) (class y) (class z))
"[Z" (boolean-array 1) (booleans (boolean-array 1 true))
"[B" (byte-array 1) (bytes (byte-array 1 (byte 1)))
"[C" (char-array 1) (chars (char-array 1 \a))
"[S" (short-array 1) (shorts (short-array 1 (short 1)))
"[F" (float-array 1) (floats (float-array 1 1))
"[D" (double-array 1) (doubles (double-array 1 1))
"[I" (int-array 1) (ints (int-array 1 1))
"[J" (long-array 1) (longs (long-array 1 1))))
(deftest test-ratios
(is (== (denominator 1/2) 2))
(is (== (numerator 1/2) 1))
(is (= (bigint (/ 100000000000000000000 3)) 33333333333333333333))
(is (= (long 10000000000000000000/3) 3333333333333333333)))
(deftest test-arbitrary-precision-subtract
(are [x y] (= x y)
9223372036854775808N (-' 0 -9223372036854775808)
clojure.lang.BigInt (class (-' 0 -9223372036854775808))
java.lang.Long (class (-' 0 -9223372036854775807))))
(deftest test-min-max
(testing "min/max on different numbers of floats and doubles"
(are [xmin xmax a]
(and (= (Float. xmin) (min (Float. a)))
(= (Float. xmax) (max (Float. a)))
(= xmin (min a))
(= xmax (max a)))
0.0 0.0 0.0)
(are [xmin xmax a b]
(and (= (Float. xmin) (min (Float. a) (Float. b)))
(= (Float. xmax) (max (Float. a) (Float. b)))
(= xmin (min a b))
(= xmax (max a b)))
-1.0 0.0 0.0 -1.0
-1.0 0.0 -1.0 0.0
0.0 1.0 0.0 1.0
0.0 1.0 1.0 0.0)
(are [xmin xmax a b c]
(and (= (Float. xmin) (min (Float. a) (Float. b) (Float. c)))
(= (Float. xmax) (max (Float. a) (Float. b) (Float. c)))
(= xmin (min a b c))
(= xmax (max a b c)))
-1.0 1.0 0.0 1.0 -1.0
-1.0 1.0 0.0 -1.0 1.0
-1.0 1.0 -1.0 1.0 0.0))
(testing "min/max preserves type of winner"
(is (= java.lang.Long (class (max 10))))
(is (= java.lang.Long (class (max 1.0 10))))
(is (= java.lang.Long (class (max 10 1.0))))
(is (= java.lang.Long (class (max 10 1.0 2.0))))
(is (= java.lang.Long (class (max 1.0 10 2.0))))
(is (= java.lang.Long (class (max 1.0 2.0 10))))
(is (= java.lang.Double (class (max 1 2 10.0 3 4 5))))
(is (= java.lang.Long (class (min 10))))
(is (= java.lang.Long (class (min 1.0 -10))))
(is (= java.lang.Long (class (min -10 1.0))))
(is (= java.lang.Long (class (min -10 1.0 2.0))))
(is (= java.lang.Long (class (min 1.0 -10 2.0))))
(is (= java.lang.Long (class (min 1.0 2.0 -10))))
(is (= java.lang.Double (class (min 1 2 -10.0 3 4 5))))))
(deftest clj-868
(testing "min/max: NaN is contagious"
(letfn [(fnan? [^Float x] (Float/isNaN x))
(dnan? [^double x] (Double/isNaN x))]
(are [minmax]
(are [nan? nan zero]
(every? nan? (map minmax
[ nan zero zero]
[zero nan zero]
[zero zero nan]))
fnan? Float/NaN (Float. 0.0)
dnan? Double/NaN 0.0)
min
max))))
(defn integer
"Distribution of integers biased towards the small, but
including all longs."
[]
(gen/one-of #(gen/uniform -1 32) gen/byte gen/short gen/int gen/long))
(defn longable?
[n]
(try
(long n)
true
(catch Exception _)))
(defspec integer-commutative-laws
(partial map identity)
[^{:tag `integer} a ^{:tag `integer} b]
(if (longable? (+' a b))
(assert (= (+ a b) (+ b a)
(+' a b) (+' b a)
(unchecked-add a b) (unchecked-add b a)))
(assert (= (+' a b) (+' b a))))
(if (longable? (*' a b))
(assert (= (* a b) (* b a)
(*' a b) (*' b a)
(unchecked-multiply a b) (unchecked-multiply b a)))
(assert (= (*' a b) (*' b a)))))
(defspec integer-associative-laws
(partial map identity)
[^{:tag `integer} a ^{:tag `integer} b ^{:tag `integer} c]
(if (every? longable? [(+' a b) (+' b c) (+' a b c)])
(assert (= (+ (+ a b) c) (+ a (+ b c))
(+' (+' a b) c) (+' a (+' b c))
(unchecked-add (unchecked-add a b) c) (unchecked-add a (unchecked-add b c))))
(assert (= (+' (+' a b) c) (+' a (+' b c))
(+ (+ (bigint a) b) c) (+ a (+ (bigint b) c)))))
(if (every? longable? [(*' a b) (*' b c) (*' a b c)])
(assert (= (* (* a b) c) (* a (* b c))
(*' (*' a b) c) (*' a (*' b c))
(unchecked-multiply (unchecked-multiply a b) c) (unchecked-multiply a (unchecked-multiply b c))))
(assert (= (*' (*' a b) c) (*' a (*' b c))
(* (* (bigint a) b) c) (* a (* (bigint b) c))))))
(defspec integer-distributive-laws
(partial map identity)
[^{:tag `integer} a ^{:tag `integer} b ^{:tag `integer} c]
(if (every? longable? [(*' a (+' b c)) (+' (*' a b) (*' a c))
(*' a b) (*' a c) (+' b c)])
(assert (= (* a (+ b c)) (+ (* a b) (* a c))
(*' a (+' b c)) (+' (*' a b) (*' a c))
(unchecked-multiply a (+' b c)) (+' (unchecked-multiply a b) (unchecked-multiply a c))))
(assert (= (*' a (+' b c)) (+' (*' a b) (*' a c))
(* a (+ (bigint b) c)) (+ (* (bigint a) b) (* (bigint a) c))))))
(defspec addition-undoes-subtraction
(partial map identity)
[^{:tag `integer} a ^{:tag `integer} b]
(if (longable? (-' a b))
(assert (= a
(-> a (- b) (+ b))
(-> a (unchecked-subtract b) (unchecked-add b)))))
(assert (= a
(-> a (-' b) (+' b)))))
(defspec quotient-and-remainder
(fn [a b] (sort [a b]))
[^{:tag `integer} a ^{:tag `integer} b]
(when-not (zero? (second %))
(let [[a d] %
q (quot a d)
r (rem a d)]
(assert (= a
(+ (* q d) r)
(unchecked-add (unchecked-multiply q d) r))))))
(defmacro check-warn-on-box [warn? form]
`(do (binding [*unchecked-math* :warn-on-boxed]
(is (= ~warn?
(boolean
(re-find #"^Boxed math warning"
(helper/with-err-string-writer
(helper/eval-in-temp-ns ~form)))))))
(binding [*unchecked-math* true]
(is (false?
(boolean
(re-find #"^Boxed math warning"
(helper/with-err-string-writer
(helper/eval-in-temp-ns ~form)))))))
(binding [*unchecked-math* false]
(is (false?
(boolean
(re-find #"^Boxed math warning"
(helper/with-err-string-writer
(helper/eval-in-temp-ns ~form)))))))))
(deftest warn-on-boxed
(check-warn-on-box true (#(inc %) 2))
(check-warn-on-box false (#(inc ^long %) 2))
(check-warn-on-box false (long-array 5))
(check-warn-on-box true (> (first (range 3)) 0))
(check-warn-on-box false (> ^long (first (range 3)) 0)))
(deftest comparisons
(let [small-numbers [1 1.0 (Integer. 1) (Float. 1.0) 9/10 1N 1M]
big-numbers [10 10.0 (Integer. 10) (Float. 10.0) 99/10 10N 10N]]
(doseq [small small-numbers big big-numbers]
(is (< small big))
(is (not (< big small)))
(is (not (< small small)))
(is (< (int small) (int big)))
(is (not (< (int big) (int small))))
(is (not (< (int small) (int small))))
(is (< (double small) (double big)))
(is (not (< (double big) (double small))))
(is (not (< (double small) (double small))))
(is (<= small big))
(is (<= small small))
(is (not (<= big small)))
(is (<= (int small) (int big)))
(is (<= (int small) (int small)))
(is (not (<= (int big) (int small))))
(is (<= (double small) (double big)))
(is (<= (double small) (double small)))
(is (not (<= (double big) (double small))))
(is (> big small))
(is (not (> small big)))
(is (not (> small small)))
(is (> (int big) (int small)))
(is (not (> (int small) (int big))))
(is (not (> (int small) (int small))))
(is (> (double big) (double small)))
(is (not (> (double small) (double big))))
(is (not (> (double small) (double small))))
(is (>= big small))
(is (>= small small))
(is (not (>= small big)))
(is (>= (int big) (int small)))
(is (>= (int small) (int small)))
(is (not (>= (int small) (int big))))
(is (>= (double big) (double small)))
(is (>= (double small) (double small)))
(is (not (>= (double small) (double big)))))))
(deftest test-nan-comparison
(are [x y] (= x y)
(< 1000 Double/NaN) (< 1000 (Double. Double/NaN))
(<= 1000 Double/NaN) (<= 1000 (Double. Double/NaN))
(> 1000 Double/NaN) (> 1000 (Double. Double/NaN))
(>= 1000 Double/NaN) (>= 1000 (Double. Double/NaN))))
================================================
FILE: test/clojure/test_clojure/other_functions.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.other-functions
(:use clojure.test))
; http://clojure.org/other_functions
; [= not= (tests in data_structures.clj and elsewhere)]
(deftest test-identity
; exactly 1 argument needed
; (is (thrown? IllegalArgumentException (identity)))
; (is (thrown? IllegalArgumentException (identity 1 2)))
(are [x] (= (identity x) x)
nil
false true
0 42
0.0 3.14
2/3
0M 1M
\c
"" "abc"
'sym
:kw
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
#{} #{1 2} )
; evaluation
(are [x y] (= (identity x) y)
(+ 1 2) 3
(> 5 0) true ))
(deftest test-name
(are [x y] (= x (name y))
"foo" :foo
"bar" 'bar
"quux" "quux"))
(deftest test-fnil
(let [f1 (fnil vector :a)
f2 (fnil vector :a :b)
f3 (fnil vector :a :b :c)]
(are [result input] (= result [(apply f1 input) (apply f2 input) (apply f3 input)])
[[1 2 3 4] [1 2 3 4] [1 2 3 4]] [1 2 3 4]
[[:a 2 3 4] [:a 2 3 4] [:a 2 3 4]] [nil 2 3 4]
[[:a nil 3 4] [:a :b 3 4] [:a :b 3 4]] [nil nil 3 4]
[[:a nil nil 4] [:a :b nil 4] [:a :b :c 4]] [nil nil nil 4]
[[:a nil nil nil] [:a :b nil nil] [:a :b :c nil]] [nil nil nil nil]))
(are [x y] (= x y)
((fnil + 0) nil 42) 42
((fnil conj []) nil 42) [42]
(reduce #(update-in %1 [%2] (fnil inc 0)) {}
["fun" "counting" "words" "fun"])
{"words" 1, "counting" 1, "fun" 2}
(reduce #(update-in %1 [(first %2)] (fnil conj []) (second %2)) {}
[[:a 1] [:a 2] [:b 3]])
{:b [3], :a [1 2]}))
; time assert comment doc
; partial
; comp
(deftest test-comp
(let [c0 (comp)]
(are [x] (= (identity x) (c0 x))
nil
42
[1 2 3]
#{}
:foo)
(are [x y] (= (identity x) (c0 y))
(+ 1 2 3) 6
(keyword "foo") :foo)))
; complement
(deftest test-complement
(let [not-contains? (complement contains?)]
(is (= true (not-contains? [2 3 4] 5)))
(is (= false (not-contains? [2 3 4] 2))))
(let [first-elem-not-1? (complement (fn [x] (= 1 (first x))))]
(is (= true (first-elem-not-1? [2 3])))
(is (= false (first-elem-not-1? [1 2])))))
; constantly
(deftest test-constantly
(let [c0 (constantly 10)]
(are [x] (= 10 (c0 x))
nil
42
"foo")))
;juxt
(deftest test-juxt
;; juxt for colls
(let [m0 {:a 1 :b 2}
a0 [1 2]]
(is (= [1 2] ((juxt :a :b) m0)))
(is (= [2 1] ((juxt fnext first) a0))))
;; juxt for fns
(let [a1 (fn [a] (+ 2 a))
b1 (fn [b] (* 2 b))]
(is (= [5 6] ((juxt a1 b1) 3)))))
;partial
(deftest test-partial
(let [p0 (partial inc)
p1 (partial + 20)
p2 (partial conj [1 2])]
(is (= 41 (p0 40)))
(is (= 40 (p1 20)))
(is (= [1 2 3] (p2 3)))))
; every-pred
(deftest test-every-pred
(are [result expr] (= result expr)
;; 1 pred
true ((every-pred even?))
true ((every-pred even?) 2)
true ((every-pred even?) 2 4)
true ((every-pred even?) 2 4 6)
true ((every-pred even?) 2 4 6 8)
true ((every-pred even?) 2 4 6 8 10)
false ((every-pred odd?) 2)
false ((every-pred odd?) 2 4)
false ((every-pred odd?) 2 4 6)
false ((every-pred odd?) 2 4 6 8)
false ((every-pred odd?) 2 4 6 8 10)
;; 2 preds
true ((every-pred even? number?))
true ((every-pred even? number?) 2)
true ((every-pred even? number?) 2 4)
true ((every-pred even? number?) 2 4 6)
true ((every-pred even? number?) 2 4 6 8)
true ((every-pred even? number?) 2 4 6 8 10)
false ((every-pred number? odd?) 2)
false ((every-pred number? odd?) 2 4)
false ((every-pred number? odd?) 2 4 6)
false ((every-pred number? odd?) 2 4 6 8)
false ((every-pred number? odd?) 2 4 6 8 10)
;; 2 preds, short-circuiting
false ((every-pred number? odd?) 1 :a)
false ((every-pred number? odd?) 1 3 :a)
false ((every-pred number? odd?) 1 3 5 :a)
false ((every-pred number? odd?) 1 3 5 7 :a)
false ((every-pred number? odd?) 1 :a 3 5 7)
;; 3 preds
true ((every-pred even? number? #(> % 0)))
true ((every-pred even? number? #(> % 0)) 2)
true ((every-pred even? number? #(> % 0)) 2 4)
true ((every-pred even? number? #(> % 0)) 2 4 6)
true ((every-pred even? number? #(> % 0)) 2 4 6 8)
true ((every-pred even? number? #(> % 0)) 2 4 6 8 10)
true ((every-pred number? even? #(> % 0)) 2 4 6 8 10 12)
false ((every-pred number? odd? #(> % 0)) 2)
false ((every-pred number? odd? #(> % 0)) 2 4)
false ((every-pred number? odd? #(> % 0)) 2 4 6)
false ((every-pred number? odd? #(> % 0)) 2 4 6 8)
false ((every-pred number? odd? #(> % 0)) 2 4 6 8 10)
false ((every-pred number? odd? #(> % 0)) 2 4 6 8 -10)
;; 3 preds, short-circuiting
false ((every-pred number? odd? #(> % 0)) 1 :a)
false ((every-pred number? odd? #(> % 0)) 1 3 :a)
false ((every-pred number? odd? #(> % 0)) 1 3 5 :a)
false ((every-pred number? odd? #(> % 0)) 1 3 5 7 :a)
false ((every-pred number? odd? #(> % 0)) 1 :a 3 5 7)
;; 4 preds
true ((every-pred even? number? #(> % 0) #(<= % 12)))
true ((every-pred even? number? #(> % 0) #(<= % 12)) 2)
true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4)
true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4 6)
true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4 6 8)
true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4 6 8 10)
true ((every-pred number? even? #(> % 0) #(<= % 12)) 2 4 6 8 10 12)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6 8)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 10)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 14)
;; 4 preds, short-circuiting
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 3 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 3 5 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 3 5 7 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 :a 3 5 7)
;; 5 preds
true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))))
true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2)
true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4)
true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6)
true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8)
true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10)
true ((every-pred number? even? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10 12)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 13)
;; 5 preds, short-circuiting
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 7 :a)
false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a 3 5 7)
;; truthiness
true (reduce #(and % %2)
(for [i (range 1 25)]
(apply (apply every-pred (repeat i identity))
(range i))))))
; some-fn
(deftest test-some-fn
(are [result] (identity result)
;; 1 pred
(not ((some-fn even?)))
((some-fn even?) 2)
((some-fn even?) 2 4)
((some-fn even?) 2 4 6)
((some-fn even?) 2 4 6 8)
((some-fn even?) 2 4 6 8 10)
(not ((some-fn odd?) 2))
(not ((some-fn odd?) 2 4))
(not ((some-fn odd?) 2 4 6))
(not ((some-fn odd?) 2 4 6 8))
(not ((some-fn odd?) 2 4 6 8 10))
;; 2 preds
(not ((some-fn even? number?)))
((some-fn even? number?) 2)
((some-fn even? number?) 2 4)
((some-fn even? number?) 2 4 6)
((some-fn even? number?) 2 4 6 8)
((some-fn even? number?) 2 4 6 8 10)
((some-fn number? odd?) 2)
((some-fn number? odd?) 2 4)
((some-fn number? odd?) 2 4 6)
((some-fn number? odd?) 2 4 6 8)
((some-fn number? odd?) 2 4 6 8 10)
;; 2 preds, short-circuiting
((some-fn number? odd?) 1 :a)
((some-fn number? odd?) 1 3 :a)
((some-fn number? odd?) 1 3 5 :a)
((some-fn number? odd?) 1 3 5 7 :a)
((some-fn number? odd?) 1 :a 3 5 7)
;; 3 preds
(not ((some-fn even? number? #(> % 0))))
((some-fn even? number? #(> % 0)) 2)
((some-fn even? number? #(> % 0)) 2 4)
((some-fn even? number? #(> % 0)) 2 4 6)
((some-fn even? number? #(> % 0)) 2 4 6 8)
((some-fn even? number? #(> % 0)) 2 4 6 8 10)
((some-fn number? even? #(> % 0)) 2 4 6 8 10 12)
((some-fn number? odd? #(> % 0)) 2)
((some-fn number? odd? #(> % 0)) 2 4)
((some-fn number? odd? #(> % 0)) 2 4 6)
((some-fn number? odd? #(> % 0)) 2 4 6 8)
((some-fn number? odd? #(> % 0)) 2 4 6 8 10)
((some-fn number? odd? #(> % 0)) 2 4 6 8 -10)
;; 3 preds, short-circuiting
((some-fn number? odd? #(> % 0)) 1 :a)
((some-fn number? odd? #(> % 0)) 1 3 :a)
((some-fn number? odd? #(> % 0)) 1 3 5 :a)
((some-fn number? odd? #(> % 0)) 1 :a 3 5 7)
;; 4 preds
(not ((some-fn even? number? #(> % 0) #(<= % 12))))
((some-fn even? number? #(> % 0) #(<= % 12)) 2)
((some-fn even? number? #(> % 0) #(<= % 12)) 2 4)
((some-fn even? number? #(> % 0) #(<= % 12)) 2 4 6)
((some-fn even? number? #(> % 0) #(<= % 12)) 2 4 6 8)
((some-fn even? number? #(> % 0) #(<= % 12)) 2 4 6 8 10)
((some-fn number? even? #(> % 0) #(<= % 12)) 2 4 6 8 10 12)
((some-fn number? odd? #(> % 0) #(<= % 12)) 2)
((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4)
((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6)
((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6 8)
((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 10)
((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 14)
;; 4 preds, short-circuiting
((some-fn number? odd? #(> % 0) #(<= % 12)) 1 :a)
((some-fn number? odd? #(> % 0) #(<= % 12)) 1 3 :a)
((some-fn number? odd? #(> % 0) #(<= % 12)) 1 3 5 :a)
((some-fn number? odd? #(> % 0) #(<= % 12)) 1 3 5 7 :a)
((some-fn number? odd? #(> % 0) #(<= % 12)) 1 :a 3 5 7)
;; 5 preds
(not ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2)))))
((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2)
((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4)
((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6)
((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8)
((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10)
((some-fn number? even? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10 12)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 13)
;; 5 preds, short-circuiting
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 :a)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 :a)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 7 :a)
((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a 3 5 7)
;; truthiness
(reduce #(or % %2)
(conj
(vec
(for [i (range 1 25)]
(apply (apply some-fn (repeat i (comp not boolean))) (range i))))
true))))
; Printing
; pr prn print println newline
; pr-str prn-str print-str println-str [with-out-str (vars.clj)]
; Regex Support
; re-matcher re-find re-matches re-groups re-seq
; update
(deftest test-update
(are [result expr] (= result expr)
{:a [1 2]} (update {:a [1]} :a conj 2)
[1] (update [0] 0 inc)
;; higher-order usage
{:a {:b 2}} (update-in {:a {:b 1}} [:a] update :b inc)
;; missing field = nil
{:a 1 :b nil} (update {:a 1} :b identity)
;; 4 hard-coded arities
{:a 1} (update {:a 1} :a +)
{:a 2} (update {:a 1} :a + 1)
{:a 3} (update {:a 1} :a + 1 1)
{:a 4} (update {:a 1} :a + 1 1 1)
;; rest arity
{:a 5} (update {:a 1} :a + 1 1 1 1)
{:a 6} (update {:a 1} :a + 1 1 1 1 1)))
================================================
FILE: test/clojure/test_clojure/parallel.clj
================================================
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Frantisek Sodomka
(ns clojure.test-clojure.parallel
(:use clojure.test))
;; !! Tests for the parallel library will be in a separate file clojure_parallel.clj !!
; future-call
; future
; pmap
; pcalls
; pvalues
;; pmap
;;
(deftest pmap-does-its-thing
;; regression fixed in r1218; was OutOfMemoryError
(is (= '(1) (pmap inc [0]))))
(def ^:dynamic *test-value* 1)
(deftest future-fn-properly-retains-conveyed-bindings
(let [a (atom [])]
(binding [*test-value* 2]
@(future (dotimes [_ 3]
;; we need some binding to trigger binding pop
(binding [*print-dup* false]
(swap! a conj *test-value*))))
(is (= [2 2 2] @a)))))
================================================
FILE: test/clojure/test_clojure/pprint/test_cl_format.clj
================================================
;;; test_cl_format.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; This test set tests the basic cl-format functionality
(in-ns 'clojure.test-clojure.pprint)
(def format cl-format)
;; TODO tests for ~A, ~D, etc.
;; TODO add tests for ~F, etc.: 0.0, 9.9999 with rounding, 9.9999E99 with rounding
(simple-tests d-tests
(cl-format nil "~D" 0) "0"
(cl-format nil "~D" 2e6) "2000000"
(cl-format nil "~D" 2000000) "2000000"
(cl-format nil "~:D" 2000000) "2,000,000"
(cl-format nil "~D" 1/2) "1/2"
(cl-format nil "~D" 'fred) "fred"
)
(simple-tests base-tests
(cl-format nil "~{~2r~^ ~}~%" (range 10))
"0 1 10 11 100 101 110 111 1000 1001\n"
(with-out-str
(dotimes [i 35]
(binding [*print-base* (+ i 2)] ;print the decimal number 40
(write 40) ;in each base from 2 to 36
(if (zero? (mod i 10)) (prn) (cl-format true " ")))))
"101000
1111 220 130 104 55 50 44 40 37 34
31 2c 2a 28 26 24 22 20 1j 1i
1h 1g 1f 1e 1d 1c 1b 1a 19 18
17 16 15 14 "
(with-out-str
(doseq [pb [2 3 8 10 16]]
(binding [*print-radix* true ;print the integer 10 and
*print-base* pb] ;the ratio 1/10 in bases 2,
(cl-format true "~&~S ~S~%" 10 1/10)))) ;3, 8, 10, 16
"#b1010 #b1/1010\n#3r101 #3r1/101\n#o12 #o1/12\n10. #10r1/10\n#xa #x1/a\n")
(simple-tests cardinal-tests
(cl-format nil "~R" 0) "zero"
(cl-format nil "~R" 4) "four"
(cl-format nil "~R" 15) "fifteen"
(cl-format nil "~R" -15) "minus fifteen"
(cl-format nil "~R" 25) "twenty-five"
(cl-format nil "~R" 20) "twenty"
(cl-format nil "~R" 200) "two hundred"
(cl-format nil "~R" 203) "two hundred three"
(cl-format nil "~R" 44879032)
"forty-four million, eight hundred seventy-nine thousand, thirty-two"
(cl-format nil "~R" -44879032)
"minus forty-four million, eight hundred seventy-nine thousand, thirty-two"
(cl-format nil "~R = ~:*~:D" 44000032)
"forty-four million, thirty-two = 44,000,032"
(cl-format nil "~R = ~:*~:D" 448790329480948209384389429384029384029842098420989842094)
"four hundred forty-eight septendecillion, seven hundred ninety sexdecillion, three hundred twenty-nine quindecillion, four hundred eighty quattuordecillion, nine hundred forty-eight tredecillion, two hundred nine duodecillion, three hundred eighty-four undecillion, three hundred eighty-nine decillion, four hundred twenty-nine nonillion, three hundred eighty-four octillion, twenty-nine septillion, three hundred eighty-four sextillion, twenty-nine quintillion, eight hundred forty-two quadrillion, ninety-eight trillion, four hundred twenty billion, nine hundred eighty-nine million, eight hundred forty-two thousand, ninety-four = 448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094"
(cl-format nil "~R = ~:*~:D" 448790329480948209384389429384029384029842098420989842094490320942058747587584758375847593475)
"448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094,490,320,942,058,747,587,584,758,375,847,593,475 = 448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094,490,320,942,058,747,587,584,758,375,847,593,475"
(cl-format nil "~R = ~:*~:D" 2e6)
"two million = 2,000,000"
(cl-format nil "~R = ~:*~:D" 200000200000)
"two hundred billion, two hundred thousand = 200,000,200,000")
(simple-tests ordinal-tests
(cl-format nil "~:R" 0) "zeroth"
(cl-format nil "~:R" 4) "fourth"
(cl-format nil "~:R" 15) "fifteenth"
(cl-format nil "~:R" -15) "minus fifteenth"
(cl-format nil "~:R" 25) "twenty-fifth"
(cl-format nil "~:R" 20) "twentieth"
(cl-format nil "~:R" 200) "two hundredth"
(cl-format nil "~:R" 203) "two hundred third"
(cl-format nil "~:R" 44879032)
"forty-four million, eight hundred seventy-nine thousand, thirty-second"
(cl-format nil "~:R" -44879032)
"minus forty-four million, eight hundred seventy-nine thousand, thirty-second"
(cl-format nil "~:R = ~:*~:D" 44000032)
"forty-four million, thirty-second = 44,000,032"
(cl-format nil "~:R = ~:*~:D" 448790329480948209384389429384029384029842098420989842094)
"four hundred forty-eight septendecillion, seven hundred ninety sexdecillion, three hundred twenty-nine quindecillion, four hundred eighty quattuordecillion, nine hundred forty-eight tredecillion, two hundred nine duodecillion, three hundred eighty-four undecillion, three hundred eighty-nine decillion, four hundred twenty-nine nonillion, three hundred eighty-four octillion, twenty-nine septillion, three hundred eighty-four sextillion, twenty-nine quintillion, eight hundred forty-two quadrillion, ninety-eight trillion, four hundred twenty billion, nine hundred eighty-nine million, eight hundred forty-two thousand, ninety-fourth = 448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094"
(cl-format nil "~:R = ~:*~:D" 448790329480948209384389429384029384029842098420989842094490320942058747587584758375847593475)
"448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094,490,320,942,058,747,587,584,758,375,847,593,475th = 448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094,490,320,942,058,747,587,584,758,375,847,593,475"
(cl-format nil "~:R = ~:*~:D" 448790329480948209384389429384029384029842098420989842094490320942058747587584758375847593471)
"448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094,490,320,942,058,747,587,584,758,375,847,593,471st = 448,790,329,480,948,209,384,389,429,384,029,384,029,842,098,420,989,842,094,490,320,942,058,747,587,584,758,375,847,593,471"
(cl-format nil "~:R = ~:*~:D" 2e6)
"two millionth = 2,000,000")
(simple-tests ordinal1-tests
(cl-format nil "~:R" 1) "first"
(cl-format nil "~:R" 11) "eleventh"
(cl-format nil "~:R" 21) "twenty-first"
(cl-format nil "~:R" 20) "twentieth"
(cl-format nil "~:R" 220) "two hundred twentieth"
(cl-format nil "~:R" 200) "two hundredth"
(cl-format nil "~:R" 999) "nine hundred ninety-ninth"
)
(simple-tests roman-tests
(cl-format nil "~@R" 3) "III"
(cl-format nil "~@R" 4) "IV"
(cl-format nil "~@R" 9) "IX"
(cl-format nil "~@R" 29) "XXIX"
(cl-format nil "~@R" 429) "CDXXIX"
(cl-format nil "~@:R" 429) "CCCCXXVIIII"
(cl-format nil "~@:R" 3429) "MMMCCCCXXVIIII"
(cl-format nil "~@R" 3429) "MMMCDXXIX"
(cl-format nil "~@R" 3479) "MMMCDLXXIX"
(cl-format nil "~@R" 3409) "MMMCDIX"
(cl-format nil "~@R" 300) "CCC"
(cl-format nil "~@R ~D" 300 20) "CCC 20"
(cl-format nil "~@R" 5000) "5,000"
(cl-format nil "~@R ~D" 5000 20) "5,000 20"
(cl-format nil "~@R" "the quick") "the quick")
(simple-tests c-tests
(cl-format nil "~{~c~^, ~}~%" "hello") "h, e, l, l, o\n"
(cl-format nil "~{~:c~^, ~}~%" "hello") "h, e, l, l, o\n"
(cl-format nil "~@C~%" \m) "\\m\n"
(cl-format nil "~@C~%" (char 222)) "\\Þ\n"
(cl-format nil "~@C~%" (char 8)) "\\backspace\n"
(cl-format nil "~@C~%" (char 3)) "\\\n")
(simple-tests e-tests
(cl-format nil "*~E*" 0.0) "*0.0E+0*"
(cl-format nil "*~6E*" 0.0) "*0.0E+0*"
(cl-format nil "*~6,0E*" 0.0) "* 0.E+0*"
(cl-format nil "*~7,2E*" 0.0) "*0.00E+0*"
(cl-format nil "*~5E*" 0.0) "*0.E+0*"
(cl-format nil "*~10,2,2,,'?E*" 2.8E120) "*??????????*"
(cl-format nil "*~10,2E*" 9.99999) "* 1.00E+1*"
(cl-format nil "*~10,2E*" 9.99999E99) "* 1.00E+100*"
(cl-format nil "*~10,2,2E*" 9.99999E99) "* 1.00E+100*"
(cl-format nil "*~10,2,2,,'?E*" 9.99999E99) "*??????????*"
)
(simple-tests $-tests
(cl-format nil "~$" 22.3) "22.30"
(cl-format nil "~$" 22.375) "22.38"
(cl-format nil "~3,5$" 22.375) "00022.375"
(cl-format nil "~3,5,8$" 22.375) "00022.375"
(cl-format nil "~3,5,10$" 22.375) " 00022.375"
(cl-format nil "~3,5,14@$" 22.375) " +00022.375"
(cl-format nil "~3,5,14@$" 22.375) " +00022.375"
(cl-format nil "~3,5,14@:$" 22.375) "+ 00022.375"
(cl-format nil "~3,,14@:$" 0.375) "+ 0.375"
(cl-format nil "~1,1$" -12.0) "-12.0"
(cl-format nil "~1,1$" 12.0) "12.0"
(cl-format nil "~1,1$" 12.0) "12.0"
(cl-format nil "~1,1@$" 12.0) "+12.0"
(cl-format nil "~1,1,8,' @:$" 12.0) "+ 12.0"
(cl-format nil "~1,1,8,' @$" 12.0) " +12.0"
(cl-format nil "~1,1,8,' :$" 12.0) " 12.0"
(cl-format nil "~1,1,8,' $" 12.0) " 12.0"
(cl-format nil "~1,1,8,' @:$" -12.0) "- 12.0"
(cl-format nil "~1,1,8,' @$" -12.0) " -12.0"
(cl-format nil "~1,1,8,' :$" -12.0) "- 12.0"
(cl-format nil "~1,1,8,' $" -12.0) " -12.0"
(cl-format nil "~1,1$" 0.001) "0.0"
(cl-format nil "~2,1$" 0.001) "0.00"
(cl-format nil "~1,1,6$" 0.001) " 0.0"
(cl-format nil "~1,1,6$" 0.0015) " 0.0"
(cl-format nil "~2,1,6$" 0.005) " 0.01"
(cl-format nil "~2,1,6$" 0.01) " 0.01"
(cl-format nil "~$" 0.099) "0.10"
(cl-format nil "~1$" 0.099) "0.1"
(cl-format nil "~1$" 0.1) "0.1"
(cl-format nil "~1$" 0.99) "1.0"
(cl-format nil "~1$" -0.99) "-1.0")
(simple-tests f-tests
(cl-format nil "~,1f" -12.0) "-12.0"
(cl-format nil "~,0f" 9.4) "9."
(cl-format nil "~,0f" 9.5) "10."
(cl-format nil "~,0f" -0.99) "-1."
(cl-format nil "~,1f" -0.99) "-1.0"
(cl-format nil "~,2f" -0.99) "-0.99"
(cl-format nil "~,3f" -0.99) "-0.990"
(cl-format nil "~,0f" 0.99) "1."
(cl-format nil "~,1f" 0.99) "1.0"
(cl-format nil "~,2f" 0.99) "0.99"
(cl-format nil "~,3f" 0.99) "0.990"
(cl-format nil "~,3f" -0.099) "-0.099"
(cl-format nil "~,4f" -0.099) "-0.0990"
(cl-format nil "~,5f" -0.099) "-0.09900"
(cl-format nil "~,3f" 0.099) "0.099"
(cl-format nil "~,4f" 0.099) "0.0990"
(cl-format nil "~,5f" 0.099) "0.09900"
(cl-format nil "~f" -1) "-1.0"
(cl-format nil "~2f" -1) "-1."
(cl-format nil "~3f" -1) "-1."
(cl-format nil "~4f" -1) "-1.0"
(cl-format nil "~8f" -1) " -1.0"
(cl-format nil "~2f" -0.0099) "-0."
(cl-format nil "~3f" -0.0099) "-0."
(cl-format nil "~4f" -0.0099) "-.01"
(cl-format nil "~5f" -0.0099) "-0.01"
(cl-format nil "~6f" -0.0099) "-.0099"
(cl-format nil "~1f" 0.0099) "0."
(cl-format nil "~2f" 0.0099) "0."
(cl-format nil "~3f" 0.0099) ".01"
(cl-format nil "~4f" 0.0099) "0.01"
(cl-format nil "~5f" 0.0099) ".0099"
(cl-format nil "~6f" 0.0099) "0.0099"
(cl-format nil "~1f" -0.099) "-.1"
(cl-format nil "~2f" -0.099) "-.1"
(cl-format nil "~3f" -0.099) "-.1"
(cl-format nil "~4f" -0.099) "-0.1"
(cl-format nil "~5f" -0.099) "-.099"
(cl-format nil "~6f" -0.099) "-0.099"
(cl-format nil "~1f" 0.099) ".1"
(cl-format nil "~2f" 0.099) ".1"
(cl-format nil "~3f" 0.099) "0.1"
(cl-format nil "~4f" 0.099) ".099"
(cl-format nil "~5f" 0.099) "0.099"
(cl-format nil "~1f" -0.99) "-1."
(cl-format nil "~2f" -0.99) "-1."
(cl-format nil "~3f" -0.99) "-1."
(cl-format nil "~4f" -0.99) "-.99"
(cl-format nil "~5f" -0.99) "-0.99"
(cl-format nil "~1f" 0.99) "1."
(cl-format nil "~2f" 0.99) "1."
(cl-format nil "~3f" 0.99) ".99"
(cl-format nil "~4f" 0.99) "0.99"
(cl-format nil "~1f" 111.11111) "111."
(cl-format nil "~4f" 111.11111) "111."
(cl-format nil "~5f" 111.11111) "111.1"
(cl-format nil "~1f" -111.11111) "-111."
(cl-format nil "~5f" -111.11111) "-111."
(cl-format nil "~6f" -111.11111) "-111.1"
(cl-format nil "~1f" 555.55555) "556."
(cl-format nil "~4f" 555.55555) "556."
(cl-format nil "~5f" 555.55555) "555.6"
(cl-format nil "~8f" 555.55555) "555.5556"
(cl-format nil "~1f" -555.55555) "-556."
(cl-format nil "~5f" -555.55555) "-556."
(cl-format nil "~6f" -555.55555) "-555.6"
(cl-format nil "~8f" -555.55555) "-555.556"
(cl-format nil "~1f" 999.999) "1000."
(cl-format nil "~5f" 999.999) "1000."
(cl-format nil "~6f" 999.999) "1000.0"
(cl-format nil "~7f" 999.999) "999.999"
(cl-format nil "~8f" 999.999) " 999.999"
(cl-format nil "~1f" -999.999) "-1000."
(cl-format nil "~6f" -999.999) "-1000."
(cl-format nil "~7f" -999.999) "-1000.0"
(cl-format nil "~8f" -999.999) "-999.999"
(cl-format nil "~5,2f" 111.11111) "111.11"
(cl-format nil "~3,1f" -0.0099) "-.0"
(cl-format nil "~6,4f" -0.0099) "-.0099"
(cl-format nil "~6,5f" -0.0099) "-.00990"
(cl-format nil "~6,6f" -0.0099) "-.009900"
(cl-format nil "~6,4f" 0.0099) "0.0099"
(cl-format nil "~6,5f" 0.0099) ".00990"
(cl-format nil "~6,6f" 0.0099) ".009900"
(cl-format nil "~2,1f" 0.0099) ".0"
(cl-format nil "~6,2f" -111.11111) "-111.11"
(cl-format nil "~6,3f" -111.11111) "-111.111"
(cl-format nil "~8,5f" -111.11111) "-111.11111"
(cl-format nil "~12,10f" 1.23456789014) "1.2345678901"
(cl-format nil "~12,10f" 1.23456789016) "1.2345678902"
(cl-format nil "~13,10f" -1.23456789014) "-1.2345678901"
(cl-format nil "~13,10f" -1.23456789016) "-1.2345678902"
(cl-format nil "~1,1f" 0.1) ".1")
(simple-tests ampersand-tests
(cl-format nil "The quick brown ~a jumped over ~d lazy dogs" 'elephant 5)
"The quick brown elephant jumped over 5 lazy dogs"
(cl-format nil "The quick brown ~&~a jumped over ~d lazy dogs" 'elephant 5)
"The quick brown \nelephant jumped over 5 lazy dogs"
(cl-format nil (platform-newlines "The quick brown ~&~a jumped\n~& over ~d lazy dogs") 'elephant 5)
"The quick brown \nelephant jumped\n over 5 lazy dogs"
(cl-format nil (platform-newlines "~&The quick brown ~&~a jumped\n~& over ~d lazy dogs") 'elephant 5)
"The quick brown \nelephant jumped\n over 5 lazy dogs"
(cl-format nil (platform-newlines "~3&The quick brown ~&~a jumped\n~& over ~d lazy dogs") 'elephant 5)
"\n\nThe quick brown \nelephant jumped\n over 5 lazy dogs"
(cl-format nil "~@{~&The quick brown ~a jumped over ~d lazy dogs~}" 'elephant 5 'fox 10)
"The quick brown elephant jumped over 5 lazy dogs\nThe quick brown fox jumped over 10 lazy dogs"
(cl-format nil "I ~[don't ~:;d~&o ~]have one~%" 0) "I don't have one\n"
(cl-format nil "I ~[don't ~:;d~&o ~]have one~%" 1) "I d\no have one\n")
(simple-tests t-tests
(cl-format nil "~@{~&~A~8,4T~:*~A~}"
'a 'aa 'aaa 'aaaa 'aaaaa 'aaaaaa 'aaaaaaa 'aaaaaaaa 'aaaaaaaaa 'aaaaaaaaaa)
"a a\naa aa\naaa aaa\naaaa aaaa\naaaaa aaaaa\naaaaaa aaaaaa\naaaaaaa aaaaaaa\naaaaaaaa aaaaaaaa\naaaaaaaaa aaaaaaaaa\naaaaaaaaaa aaaaaaaaaa"
(cl-format nil "~@{~&~A~,4T~:*~A~}"
'a 'aa 'aaa 'aaaa 'aaaaa 'aaaaaa 'aaaaaaa 'aaaaaaaa 'aaaaaaaaa 'aaaaaaaaaa)
"a a\naa aa\naaa aaa\naaaa aaaa\naaaaa aaaaa\naaaaaa aaaaaa\naaaaaaa aaaaaaa\naaaaaaaa aaaaaaaa\naaaaaaaaa aaaaaaaaa\naaaaaaaaaa aaaaaaaaaa"
(cl-format nil "~@{~&~A~2,6@T~:*~A~}" 'a 'aa 'aaa 'aaaa 'aaaaa 'aaaaaa 'aaaaaaa 'aaaaaaaa 'aaaaaaaaa 'aaaaaaaaaa)
"a a\naa aa\naaa aaa\naaaa aaaa\naaaaa aaaaa\naaaaaa aaaaaa\naaaaaaa aaaaaaa\naaaaaaaa aaaaaaaa\naaaaaaaaa aaaaaaaaa\naaaaaaaaaa aaaaaaaaaa"
)
(simple-tests paren-tests
(cl-format nil "~(PLEASE SPEAK QUIETLY IN HERE~)") "please speak quietly in here"
(cl-format nil "~@(PLEASE SPEAK QUIETLY IN HERE~)") "Please speak quietly in here"
(cl-format nil "~@:(but this Is imporTant~)") "BUT THIS IS IMPORTANT"
(cl-format nil "~:(the greAt gatsby~)!") "The Great Gatsby!"
;; Test cases from CLtL 18.3 - string-upcase, et al.
(cl-format nil "~@:(~A~)" "Dr. Livingstone, I presume?") "DR. LIVINGSTONE, I PRESUME?"
(cl-format nil "~(~A~)" "Dr. Livingstone, I presume?") "dr. livingstone, i presume?"
(cl-format nil "~:(~A~)" " hello ") " Hello "
(cl-format nil "~:(~A~)" "occlUDeD cASEmenTs FOreSTAll iNADVertent DEFenestraTION")
"Occluded Casements Forestall Inadvertent Defenestration"
(cl-format nil "~:(~A~)" 'kludgy-hash-search) "Kludgy-Hash-Search"
(cl-format nil "~:(~A~)" "DON'T!") "Don'T!" ;not "Don't!"
(cl-format nil "~:(~A~)" "pipe 13a, foo16c") "Pipe 13a, Foo16c"
(cl-format nil "~:(~A~)" nil) "Nil"
(cl-format nil "~:(~A~)" "") ""
)
(simple-tests square-bracket-tests
;; Tests for format without modifiers
(cl-format nil "I ~[don't ~]have one~%" 0) "I don't have one\n"
(cl-format nil "I ~[don't ~]have one~%" 1) "I have one\n"
(cl-format nil "I ~[don't ~;do ~]have one~%" 0) "I don't have one\n"
(cl-format nil "I ~[don't ~;do ~]have one~%" 1) "I do have one\n"
(cl-format nil "I ~[don't ~;do ~]have one~%" 2) "I have one\n"
(cl-format nil "I ~[don't ~:;do ~]have one~%" 0) "I don't have one\n"
(cl-format nil "I ~[don't ~:;do ~]have one~%" 1) "I do have one\n"
(cl-format nil "I ~[don't ~:;do ~]have one~%" 2) "I do have one\n"
(cl-format nil "I ~[don't ~:;do ~]have one~%" 700) "I do have one\n"
;; Tests for format with a colon
(cl-format nil "I ~:[don't ~;do ~]have one~%" true) "I do have one\n"
(cl-format nil "I ~:[don't ~;do ~]have one~%" 700) "I do have one\n"
(cl-format nil "I ~:[don't ~;do ~]have one~%" '(a b)) "I do have one\n"
(cl-format nil "I ~:[don't ~;do ~]have one~%" nil) "I don't have one\n"
(cl-format nil "I ~:[don't ~;do ~]have one~%" false) "I don't have one\n"
;; Tests for format with an at sign
(cl-format nil "We had ~D wins~@[ (out of ~D tries)~].~%" 15 nil) "We had 15 wins.\n"
(cl-format nil "We had ~D wins~@[ (out of ~D tries)~].~%" 15 17)
"We had 15 wins (out of 17 tries).\n"
;; Format tests with directives
(cl-format nil "Max ~D: ~[Blue team ~D~;Red team ~D~:;No team ~A~].~%" 15, 0, 7)
"Max 15: Blue team 7.\n"
(cl-format nil "Max ~D: ~[Blue team ~D~;Red team ~D~:;No team ~A~].~%" 15, 1, 12)
"Max 15: Red team 12.\n"
(cl-format nil "Max ~D: ~[Blue team ~D~;Red team ~D~:;No team ~A~].~%"
15, -1, "(system failure)")
"Max 15: No team (system failure).\n"
;; Nested format tests
(cl-format nil "Max ~D: ~[Blue team ~D~:[~; (complete success)~]~;Red team ~D~:;No team ~].~%"
15, 0, 7, true)
"Max 15: Blue team 7 (complete success).\n"
(cl-format nil "Max ~D: ~[Blue team ~D~:[~; (complete success)~]~;Red team ~D~:;No team ~].~%"
15, 0, 7, false)
"Max 15: Blue team 7.\n"
;; Test the selector as part of the argument
(cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~].")
"The answer is nothing."
(cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~]." 4)
"The answer is 4."
(cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~]." 7 22)
"The answer is 7 out of 22."
(cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~]." 1 2 3 4)
"The answer is something crazy."
)
(simple-tests curly-brace-plain-tests
;; Iteration from sublist
(cl-format nil "Coordinates are~{ [~D,~D]~}~%" [ 0, 1, 1, 0, 3, 5, 2, 1 ])
"Coordinates are [0,1] [1,0] [3,5] [2,1]\n"
(cl-format nil "Coordinates are~2{ [~D,~D]~}~%" [ 0, 1, 1, 0, 3, 5, 2, 1 ])
"Coordinates are [0,1] [1,0]\n"
(cl-format nil "Coordinates are~{ ~#[none~;<~D>~:;[~D,~D]~]~}~%" [ ])
"Coordinates are\n"
(cl-format nil "Coordinates are~{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [ ])
"Coordinates are none\n"
(cl-format nil "Coordinates are~{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [2 3 1])
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~{~:}~%" "" [])
"Coordinates are\n"
(cl-format nil "Coordinates are~{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [2 3 1])
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [ ])
"Coordinates are none\n"
)
(simple-tests curly-brace-colon-tests
;; Iteration from list of sublists
(cl-format nil "Coordinates are~:{ [~D,~D]~}~%" [ [0, 1], [1, 0], [3, 5], [2, 1] ])
"Coordinates are [0,1] [1,0] [3,5] [2,1]\n"
(cl-format nil "Coordinates are~:{ [~D,~D]~}~%" [ [0, 1, 0], [1, 0, 12], [3, 5], [2, 1] ])
"Coordinates are [0,1] [1,0] [3,5] [2,1]\n"
(cl-format nil "Coordinates are~2:{ [~D,~D]~}~%" [ [0, 1], [1, 0], [3, 5], [2, 1] ])
"Coordinates are [0,1] [1,0]\n"
(cl-format nil "Coordinates are~:{ ~#[none~;<~D>~:;[~D,~D]~]~}~%" [ ])
"Coordinates are\n"
(cl-format nil "Coordinates are~:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [ ])
"Coordinates are none\n"
(cl-format nil "Coordinates are~:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [[2 3] [1]])
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~:{~:}~%" "" [])
"Coordinates are\n"
(cl-format nil "Coordinates are~:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [[2 3] [1]])
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [ ])
"Coordinates are none\n"
)
(simple-tests curly-brace-at-tests
;; Iteration from main list
(cl-format nil "Coordinates are~@{ [~D,~D]~}~%" 0, 1, 1, 0, 3, 5, 2, 1)
"Coordinates are [0,1] [1,0] [3,5] [2,1]\n"
(cl-format nil "Coordinates are~2@{ [~D,~D]~}~%" 0, 1, 1, 0, 3, 5, 2, 1)
"Coordinates are [0,1] [1,0]\n"
(cl-format nil "Coordinates are~@{ ~#[none~;<~D>~:;[~D,~D]~]~}~%")
"Coordinates are\n"
(cl-format nil "Coordinates are~@{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%")
"Coordinates are none\n"
(cl-format nil "Coordinates are~@{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" 2 3 1)
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~@{~:}~%" "")
"Coordinates are\n"
(cl-format nil "Coordinates are~@{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" 2 3 1)
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~@{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]")
"Coordinates are none\n"
)
(simple-tests curly-brace-colon-at-tests
;; Iteration from sublists on the main arg list
(cl-format nil "Coordinates are~@:{ [~D,~D]~}~%" [0, 1], [1, 0], [3, 5], [2, 1] )
"Coordinates are [0,1] [1,0] [3,5] [2,1]\n"
(cl-format nil "Coordinates are~@:{ [~D,~D]~}~%" [0, 1, 0], [1, 0, 12], [3, 5], [2, 1] )
"Coordinates are [0,1] [1,0] [3,5] [2,1]\n"
(cl-format nil "Coordinates are~2@:{ [~D,~D]~}~%" [0, 1], [1, 0], [3, 5], [2, 1])
"Coordinates are [0,1] [1,0]\n"
(cl-format nil "Coordinates are~@:{ ~#[none~;<~D>~:;[~D,~D]~]~}~%")
"Coordinates are\n"
(cl-format nil "Coordinates are~@:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%")
"Coordinates are none\n"
(cl-format nil "Coordinates are~@:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [2 3] [1])
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~@:{~:}~%" "")
"Coordinates are\n"
(cl-format nil "Coordinates are~@:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [2 3] [1])
"Coordinates are [2,3] <1>\n"
(cl-format nil "Coordinates are~@:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]")
"Coordinates are none\n"
)
;; TODO tests for ~^ in ~[ constructs and other brackets
;; TODO test ~:^ generates an error when used improperly
;; TODO test ~:^ works in ~@:{...~}
(let [aseq '(a quick brown fox jumped over the lazy dog)
lseq (mapcat identity (for [x aseq] [x (.length (name x))]))]
(simple-tests up-tests
(cl-format nil "~{~a~^, ~}" aseq) "a, quick, brown, fox, jumped, over, the, lazy, dog"
(cl-format nil "~{~a~0^, ~}" aseq) "a"
(cl-format nil "~{~a~#,3^, ~}" aseq) "a, quick, brown, fox, jumped, over"
(cl-format nil "~{~a~v,3^, ~}" lseq) "a, quick, brown, fox"
(cl-format nil "~{~a~3,v,4^, ~}" lseq) "a, quick, brown, fox"
))
(simple-tests angle-bracket-tests
(cl-format nil "~") "foobarbaz"
(cl-format nil "~20") "foo bar baz"
(cl-format nil "~,,2") "foo bar baz"
(cl-format nil "~20<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz"
(cl-format nil "~20:<~A~;~A~;~A~>" "foo" "bar" "baz") " foo bar baz"
(cl-format nil "~20@<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz "
(cl-format nil "~20@:<~A~;~A~;~A~>" "foo" "bar" "baz") " foo bar baz "
(cl-format nil "~10,,2<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz"
(cl-format nil "~10,10,2<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz"
(cl-format nil "~10,10<~A~;~A~;~A~>" "foo" "bar" "baz") "foo barbaz"
(cl-format nil "~20<~A~;~^~A~;~^~A~>" "foo" "bar" "baz") "foo bar baz"
(cl-format nil "~20<~A~;~^~A~;~^~A~>" "foo" "bar") "foo bar"
(cl-format nil "~20@<~A~;~^~A~;~^~A~>" "foo") "foo "
(cl-format nil "~20:<~A~;~^~A~;~^~A~>" "foo") " foo"
)
(simple-tests angle-bracket-max-column-tests
(cl-format nil "~%;; ~{~<~%;; ~1,50:; ~A~>~}.~%" (into [] (.split "This function computes the circular thermodynamic coefficient of the thrombulator angle for use in determining the reaction distance" "\\s")))
"\n;; This function computes the circular\n;; thermodynamic coefficient of the thrombulator\n;; angle for use in determining the reaction\n;; distance.\n"
(cl-format true "~%;; ~{~<~%;; ~:; ~A~>~}.~%" (into [] (.split "This function computes the circular thermodynamic coefficient of the thrombulator angle for use in determining the reaction distance." "\\s"))))
(defn list-to-table [aseq column-width]
(let [stream (get-pretty-writer (java.io.StringWriter.))]
(binding [*out* stream]
(doseq [row aseq]
(doseq [col row]
(cl-format true "~4D~7,vT" col column-width))
(prn)))
(.flush stream)
(.toString (:base @@(:base @@stream)))))
(simple-tests column-writer-test
(list-to-table (map #(vector % (* % %) (* % % %)) (range 1 21)) 8)
" 1 1 1 \n 2 4 8 \n 3 9 27 \n 4 16 64 \n 5 25 125 \n 6 36 216 \n 7 49 343 \n 8 64 512 \n 9 81 729 \n 10 100 1000 \n 11 121 1331 \n 12 144 1728 \n 13 169 2197 \n 14 196 2744 \n 15 225 3375 \n 16 256 4096 \n 17 289 4913 \n 18 324 5832 \n 19 361 6859 \n 20 400 8000 \n")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The following tests are the various examples from the format
;; documentation in Common Lisp, the Language, 2nd edition, Chapter 22.3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn expt [base pow] (reduce * (repeat pow base)))
(let [x 5, y "elephant", n 3]
(simple-tests cltl-intro-tests
(format nil "foo") "foo"
(format nil "The answer is ~D." x) "The answer is 5."
(format nil "The answer is ~3D." x) "The answer is 5."
(format nil "The answer is ~3,'0D." x) "The answer is 005."
(format nil "The answer is ~:D." (expt 47 x)) "The answer is 229,345,007."
(format nil "Look at the ~A!" y) "Look at the elephant!"
(format nil "Type ~:C to ~A." (char 4) "delete all your files")
"Type Control-D to delete all your files."
(format nil "~D item~:P found." n) "3 items found."
(format nil "~R dog~:[s are~; is~] here." n (= n 1)) "three dogs are here."
(format nil "~R dog~:*~[s are~; is~:;s are~] here." n) "three dogs are here."
(format nil "Here ~[are~;is~:;are~] ~:*~R pupp~:@P." n) "Here are three puppies."))
(simple-tests cltl-B-tests
;; CLtL didn't have the colons here, but the spec requires them
(format nil "~,,' ,4:B" 0xFACE) "1111 1010 1100 1110"
(format nil "~,,' ,4:B" 0x1CE) "1 1100 1110"
(format nil "~19,,' ,4:B" 0xFACE) "1111 1010 1100 1110"
;; This one was a nice idea, but nothing in the spec supports it working this way
;; (and SBCL doesn't work this way either)
;(format nil "~19,,' ,4:B" 0x1CE) "0000 0001 1100 1110")
)
(simple-tests cltl-P-tests
(format nil "~D tr~:@P/~D win~:P" 7 1) "7 tries/1 win"
(format nil "~D tr~:@P/~D win~:P" 1 0) "1 try/0 wins"
(format nil "~D tr~:@P/~D win~:P" 1 3) "1 try/3 wins")
(defn foo [x]
(format nil "~6,2F|~6,2,1,'*F|~6,2,,'?F|~6F|~,2F|~F"
x x x x x x))
;; big-pos-ratio is a ratio value that is larger than
;; Double/MAX_VALUE, and has a non-terminating decimal representation
;; if you attempt to represent it exactly.
(def big-pos-ratio (/ (* 4 (bigint (. BigDecimal valueOf Double/MAX_VALUE))) 3))
(def big-neg-ratio (- big-pos-ratio))
;; tiny-pos-ratio is a ratio between 0 and Double/MIN_VALUE.
(def tiny-pos-ratio (/ 1 (bigint (apply str (cons "1" (repeat 340 "0"))))))
(def tiny-neg-ratio (- tiny-pos-ratio))
(simple-tests cltl-F-tests
(cl-format false "~10,3f" 4/5) " 0.800"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3f" big-pos-ratio)) "239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3f" big-neg-ratio)) "-239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3f" tiny-pos-ratio)) " 0.000"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3f" tiny-neg-ratio)) " -0.000"
(foo 3.14159) " 3.14| 31.42| 3.14|3.1416|3.14|3.14159"
(foo 314159/100000)
" 3.14| 31.42| 3.14|3.1416|3.14|3.14159"
(foo -3.14159) " -3.14|-31.42| -3.14|-3.142|-3.14|-3.14159"
(foo 100.0) "100.00|******|100.00| 100.0|100.00|100.0"
(foo 1234.0) "1234.00|******|??????|1234.0|1234.00|1234.0"
(foo 0.006) " 0.01| 0.06| 0.01| 0.006|0.01|0.006")
(defn foo-e [x]
(format nil
"~9,2,1,,'*E|~10,3,2,2,'?,,'$E|~9,3,2,-2,'%@E|~9,2E"
x x x x))
;; Clojure doesn't support float/double differences in representation
(simple-tests cltl-E-tests
(cl-format false "~10,3e" 4/5) " 8.000E-1"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3e" big-pos-ratio)) "2.397E+308"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3e" big-neg-ratio)) "-2.397E+308"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3e" tiny-pos-ratio)) "1.000E-340"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3e" tiny-neg-ratio)) "-1.000E-340"
(foo-e 0.0314159) " 3.14E-2| 31.42$-03|+.003E+01| 3.14E-2" ; Added this one
(foo-e 314159/10000000)
" 3.14E-2| 31.42$-03|+.003E+01| 3.14E-2"
(foo-e 3.14159) " 3.14E+0| 31.42$-01|+.003E+03| 3.14E+0"
(foo-e -3.14159) " -3.14E+0|-31.42$-01|-.003E+03| -3.14E+0"
(foo-e 1100.0) " 1.10E+3| 11.00$+02|+.001E+06| 1.10E+3"
; In Clojure, this is identical to the above
; (foo-e 1100.0L0) " 1.10L+3| 11.00$+02|+.001L+06| 1.10L+3"
(foo-e 1.1E13) "*********| 11.00$+12|+.001E+16| 1.10E+13"
(foo-e 1.1E120) "*********|??????????|%%%%%%%%%|1.10E+120"
; Clojure doesn't support real numbers this large
; (foo-e 1.1L1200) "*********|??????????|%%%%%%%%%|1.10L+1200"
)
(simple-tests cltl-E-scale-tests
(map
(fn [k] (format nil "Scale factor ~2D~:*: |~13,6,2,VE|"
(- k 5) 3.14159)) ;Prints 13 lines
(range 13))
'("Scale factor -5: | 0.000003E+06|"
"Scale factor -4: | 0.000031E+05|"
"Scale factor -3: | 0.000314E+04|"
"Scale factor -2: | 0.003142E+03|"
"Scale factor -1: | 0.031416E+02|"
"Scale factor 0: | 0.314159E+01|"
"Scale factor 1: | 3.141590E+00|"
"Scale factor 2: | 31.41590E-01|"
"Scale factor 3: | 314.1590E-02|"
"Scale factor 4: | 3141.590E-03|"
"Scale factor 5: | 31415.90E-04|"
"Scale factor 6: | 314159.0E-05|"
"Scale factor 7: | 3141590.E-06|"))
(defn foo-g [x]
(format nil
"~9,2,1,,'*G|~9,3,2,3,'?,,'$G|~9,3,2,0,'%G|~9,2G"
x x x x))
;; Clojure doesn't support float/double differences in representation
(simple-tests cltl-G-tests
(cl-format false "~10,3g" 4/5) " 0.800 "
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3g" big-pos-ratio)) "2.397E+308"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3g" big-neg-ratio)) "-2.397E+308"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3g" tiny-pos-ratio)) "1.000E-340"
(binding [*math-context* java.math.MathContext/DECIMAL128]
(cl-format false "~10,3g" tiny-neg-ratio)) "-1.000E-340"
(foo-g 0.0314159) " 3.14E-2|314.2$-04|0.314E-01| 3.14E-2"
(foo-g 314159/10000000)
" 3.14E-2|314.2$-04|0.314E-01| 3.14E-2"
(foo-g 0.314159) " 0.31 |0.314 |0.314 | 0.31 "
(foo-g 3.14159) " 3.1 | 3.14 | 3.14 | 3.1 "
(foo-g 31.4159) " 31. | 31.4 | 31.4 | 31. "
(foo-g 314.159) " 3.14E+2| 314. | 314. | 3.14E+2"
(foo-g 3141.59) " 3.14E+3|314.2$+01|0.314E+04| 3.14E+3"
; In Clojure, this is identical to the above
; (foo-g 3141.59L0) " 3.14L+3|314.2$+01|0.314L+04| 3.14L+3"
(foo-g 3.14E12) "*********|314.0$+10|0.314E+13| 3.14E+12"
(foo-g 3.14E120) "*********|?????????|%%%%%%%%%|3.14E+120"
; Clojure doesn't support real numbers this large
; (foo-g 3.14L1200) "*********|?????????|%%%%%%%%%|3.14L+1200"
)
(defn type-clash-error [fun nargs argnum right-type wrong-type]
(format nil ;; CLtL has this format string slightly wrong
"~&Function ~S requires its ~:[~:R ~;~*~]~
argument to be of type ~S,~%but it was called ~
with an argument of type ~S.~%"
fun (= nargs 1) argnum right-type wrong-type))
(simple-tests cltl-Newline-tests
(type-clash-error 'aref nil 2 'integer 'vector)
"Function aref requires its second argument to be of type integer,
but it was called with an argument of type vector.\n"
(type-clash-error 'car 1 1 'list 'short-float)
"Function car requires its argument to be of type list,
but it was called with an argument of type short-float.\n")
(simple-tests cltl-?-tests
(format nil "~? ~D" "<~A ~D>" '("Foo" 5) 7) " 7"
(format nil "~? ~D" "<~A ~D>" '("Foo" 5 14) 7) " 7"
(format nil "~@? ~D" "<~A ~D>" "Foo" 5 7) " 7"
(format nil "~@? ~D" "<~A ~D>" "Foo" 5 14 7) " 14")
(defn f [n] (format nil "~@(~R~) error~:P detected." n))
(simple-tests cltl-paren-tests
(format nil "~@R ~(~@R~)" 14 14) "XIV xiv"
(f 0) "Zero errors detected."
(f 1) "One error detected."
(f 23) "Twenty-three errors detected.")
(let [*print-level* nil *print-length* 5]
(simple-tests cltl-bracket-tests
(format nil "~@[ print level = ~D~]~@[ print length = ~D~]"
*print-level* *print-length*)
" print length = 5"))
(let [foo "Items:~#[ none~; ~S~; ~S and ~S~
~:;~@{~#[~; and~] ~
~S~^,~}~]."]
(simple-tests cltl-bracket1-tests
(format nil foo) "Items: none."
(format nil foo 'foo) "Items: foo."
(format nil foo 'foo 'bar) "Items: foo and bar."
(format nil foo 'foo 'bar 'baz) "Items: foo, bar, and baz."
(format nil foo 'foo 'bar 'baz 'quux) "Items: foo, bar, baz, and quux."))
(simple-tests cltl-curly-bracket-tests
(format nil
"The winners are:~{ ~S~}."
'(fred harry jill))
"The winners are: fred harry jill."
(format nil "Pairs:~{ <~S,~S>~}." '(a 1 b 2 c 3))
"Pairs: