Collections standard library is now generated from templates.

This commit is contained in:
Maxim Shafirov
2013-02-12 18:49:29 +04:00
parent 324a39d10b
commit 620aae28d0
78 changed files with 3782 additions and 3890 deletions
-1
View File
@@ -351,7 +351,6 @@ class CollectionTest {
test fun lastException() {
fails { arrayList<Int>().last() }
fails { hashSet<Char>().last() }
}
test fun subscript() {
+19 -5
View File
@@ -33,15 +33,29 @@ class ListTest {
assertEquals("bar", data.last)
}
test fun withIndices() {
test fun forEachWithIndex() {
val data = arrayList("foo", "bar")
val wis = data.withIndices()
var index = 0
for (withIndex in wis) {
assertEquals(withIndex.first, index)
assertEquals(withIndex.second, data[index])
data.forEachWithIndex { (i, d) ->
assertEquals(i, index)
assertEquals(d, data[index])
index++
}
assertEquals(data.size(), index)
}
test fun withIndices() {
val data = arrayList("foo", "bar")
var index = 0
for ((i, d) in data.withIndices()) {
assertEquals(i, index)
assertEquals(d, data[index])
index++
}
assertEquals(data.size(), index)
}
@@ -1,60 +0,0 @@
package org.jetbrains.kotlin.tools
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
private fun generateDownTos(outputFile: File, header: String) {
fun getMaxType(fromType: String, toType: String): String {
return when {
fromType == "Double" || toType == "Double" -> "Double"
fromType == "Float" || toType == "Float" -> "Float"
fromType == "Long" || toType == "Long" -> "Long"
fromType == "Int" || toType == "Int" -> "Int"
fromType == "Short" || toType == "Short" -> "Short"
fromType == "Char" || toType == "Char" -> "Char"
else -> "Byte"
}
}
fun generateDownTo(writer: PrintWriter, fromType: String, toType: String) {
val elementType = getMaxType(fromType, toType)
val progressionType = elementType + "Progression"
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
val toExpr = if (elementType == toType) "to" else "to.to$elementType()"
val incrementExpr = when (elementType) {
"Long" -> "-1.toLong()"
"Float" -> "-1.toFloat()"
"Double" -> "-1.0"
else -> "-1"
}
writer.println("""
public inline fun $fromType.downTo(to: $toType): $progressionType {
return $progressionType($fromExpr, $toExpr, $incrementExpr)
}""")
}
println("Writing $outputFile")
outputFile.getParentFile()?.mkdirs()
val writer = PrintWriter(FileWriter(outputFile))
try {
writer.println(header)
writer.println("""
$COMMON_AUTOGENERATED_WARNING
""")
val types = array("Byte", "Char", "Short", "Int", "Long", "Float", "Double")
for (fromType in types) {
for (toType in types) {
generateDownTo(writer, fromType, toType)
}
}
} finally {
writer.close()
}
}
@@ -1,189 +0,0 @@
package org.jetbrains.kotlin.tools
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.ArrayList
import java.util.TreeMap
import org.w3c.dom.*
import org.w3c.dom.events.*
/**
* This tool generates JavaScript stubs for classes available in the JDK which are already available in the browser environment
* such as the W3C DOM
*/
fun generateDomAPI(file: File): Unit {
val packageName = "org.w3c.dom"
val imports = ""
val classes: List<Class<*>> = arrayList(javaClass<Attr>(), javaClass<CDATASection>(),
javaClass<CharacterData>(), javaClass<Comment>(),
javaClass<Document>(), javaClass<DocumentFragment>(), javaClass<DocumentType>(),
javaClass<DOMConfiguration>(),
javaClass<DOMError>(), javaClass<DOMErrorHandler>(),
javaClass<DOMImplementation>(), javaClass<DOMImplementationList>(),
javaClass<DOMLocator>(),
javaClass<DOMStringList>(),
javaClass<Element>(),
javaClass<Entity>(), javaClass<EntityReference>(),
javaClass<NameList>(), javaClass<NamedNodeMap>(), javaClass<Node>(), javaClass<NodeList>(),
javaClass<Notation>(), javaClass<ProcessingInstruction>(),
javaClass<Text>(), javaClass<TypeInfo>(),
javaClass<UserDataHandler>())
writeClassesFile(file, packageName, imports, classes)
}
fun generateDomEventsAPI(file: File): Unit {
val packageName = "org.w3c.dom.events"
val imports = "import org.w3c.dom.*\nimport org.w3c.dom.views.*\n"
val classes: List<Class<*>> = arrayList(javaClass<DocumentEvent>(), javaClass<Event>(),
// TODO see domEventsCode.kt we manually hand craft this for now
// to get the implementation in JS
//
// javaClass<EventListener>(),
javaClass<EventTarget>(),
javaClass<MouseEvent>(), javaClass<MutationEvent>(),
javaClass<UIEvent>())
writeClassesFile(file, packageName, imports, classes)
}
private fun writeClassesFile(file: File, packageName: String, imports: String, classes: List<Class<*>>): Unit {
write(file) {
println("""
package $packageName
$imports
//
// NOTE THIS FILE IS AUTO-GENERATED by the GenerateJavaScriptStubs.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
import js.noImpl
// Contains stub APIs for the W3C DOM API so we can delegate to the platform DOM instead
""")
fun simpleTypeName(klass: Class<out Any?>?): String {
val answer = klass?.getSimpleName()?.capitalize() ?: "Unit"
return if (answer == "Void") "Unit" else if (answer == "Object") "Any" else answer
}
fun parameterTypeName(klass: Class<out Any?>?): String {
val answer = simpleTypeName(klass)
return if (answer == "String" || answer == "Event" || answer.endsWith("DocumentType")) {
answer + "?"
} else answer
}
for (klass in classes) {
val interfaces = klass.getInterfaces()
val extends = if (interfaces != null && interfaces.size == 1) ": ${interfaces[0]?.getSimpleName()}" else ""
println("native public trait ${klass.getSimpleName()}$extends {")
val methods = klass.getDeclaredMethods()
if (methods != null) {
// lets figure out the properties versus methods
val validMethods = ArrayList<Method>()
val properties = TreeMap<String, PropertyKind>()
for (method in methods) {
if (method != null) {
val name = method.getName() ?: ""
fun propertyName(): String {
val answer = name.substring(3).decapitalize()
return if (answer == "type") {
"`type`"
} else answer
}
fun propertyType() = simpleTypeName(method.getReturnType())
fun propertyKind(method: Method): PropertyKind {
val propName = propertyName()
return properties.getOrPut(propName) { PropertyKind(propName, "val", method) }
}
val params = method.getParameterTypes()!!
val paramSize = params.size
if (name.size > 3) {
if (name.startsWith("get") && paramSize == 0) {
propertyKind(method).typeName = propertyType()
} else if (name.startsWith("set") && paramSize == 1) {
propertyKind(method).kind = "var"
} else {
validMethods.add(method)
}
} else {
validMethods.add(method)
}
}
}
for (pk in properties.values()!!) {
// some properties might not have a getter defined
// so lets ignore those
val typeName = pk.typeName
if (typeName == null) {
validMethods.add(pk.method)
} else {
println(" public ${pk.kind} ${pk.name}: ${typeName}")
}
}
for (method in validMethods) {
val parameterTypes = method.getParameterTypes()!!
// TODO in java 7 its not easy with reflection to get the parameter argument name...
var counter = 0
val parameters = parameterTypes.map{ "arg${++counter}: ${parameterTypeName(it)}" }.makeString(", ")
val returnType = simpleTypeName(method.getReturnType())
println(" public fun ${method.getName()}($parameters): $returnType = js.noImpl")
}
}
val fields = klass.getDeclaredFields()
if (fields != null) {
if (fields.notEmpty()) {
println("")
println(" public class object {")
for (field in fields) {
if (field != null) {
val modifiers = field.getModifiers()
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
val fieldType = simpleTypeName(field.getType())
try {
val value = field.get(null)
if (value != null) {
val fieldType = simpleTypeName(field.getType())
println(" public val ${field.getName()}: $fieldType = $value")
}
} catch (e: Exception) {
println("Caught: $e")
e.printStackTrace()
}
}
}
}
println(" }")
}
}
println("}")
println("")
}
}
}
class PropertyKind(val name: String, var kind: String, val method: Method, var typeName: String? = null)
fun write(file: File, block: PrintWriter.() -> Unit): Unit {
println("Generating file: ${file.getCanonicalPath()}")
val writer = PrintWriter(FileWriter(file))
writer.use { writer.block() }
}
@@ -1,154 +0,0 @@
package org.jetbrains.kotlin.tools
import java.io.*
private val COMMON_AUTOGENERATED_WARNING: String = """//
// NOTE THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//"""
fun generateFile(outFile: File, header: String, inputFile: File, f: (String)-> String) {
generateFile(outFile, header, arrayList(inputFile), f)
}
fun generateFile(outFile: File, header: String, inputFile: File, jvmFile: File, f: (String)-> String) {
generateFile(outFile, header, arrayList(inputFile, jvmFile), f)
}
fun generateFile(outFile: File, header: String, inputFiles: List<File>, f: (String)-> String) {
outFile.getParentFile()?.mkdirs()
val writer = PrintWriter(FileWriter(outFile))
try {
writer.println(header)
for (file in inputFiles) {
writer.println("""
$COMMON_AUTOGENERATED_WARNING
// Generated from input file: $file
//
""")
println("Parsing $file and writing $outFile")
val reader = FileReader(file).buffered()
try {
// TODO ideally we'd use a filterNot() here :)
val iter = reader.lineIterator()
while (iter.hasNext()) {
val line = iter.next()
if (line.startsWith("package")) continue
val xform = f(line)
writer.println(xform)
}
} finally {
reader.close()
reader.close()
}
}
} finally {
writer.close()
}
}
/**
* Generates methods in the standard library which are mostly identical
* but just using a different input kind.
*
* Kinda like mimicking source macros here, but this avoids the inefficiency of type conversions
* at runtime.
*/
fun main(args: Array<String>) {
var srcDir = File("src/kotlin")
if (!srcDir.exists()) {
srcDir = File("stdlib/src/kotlin")
require(srcDir.exists(), "Could not find the src/kotlin directory!")
}
val outDir = File(srcDir, "../generated")
val jsCoreDir = File(srcDir, "../../../../js/js.libraries/src/core")
require(jsCoreDir.exists())
generateDomAPI(File(jsCoreDir, "dom.kt"))
generateDomEventsAPI(File(jsCoreDir, "domEvents.kt"))
val otherArrayNames = arrayList("Boolean", "Byte", "Char", "Short", "Int", "Long", "Float", "Double")
// Iterables - Generic iterable stuff
generateFile(File(outDir, "ArraysFromIterables.kt"), "package kotlin\n", File(srcDir, "Iterables.kt")) {
it.replaceAll("Iterable<T", "Array<T").
replaceAll("Iterable<T", "Array<T")
}
generateFile(File(outDir, "ArraysFromIterablesJVM.kt"), "package kotlin\n", File(srcDir, "IterablesJVM.kt")) {
it.replaceAll("Iterable<T", "Array<T").
replaceAll("Iterable<T", "Array<T")
}
generateFile(File(outDir, "ArraysFromIterablesLazy.kt"), "package kotlin\n", File(srcDir, "IterablesLazy.kt")) {
it.replaceAll("Iterable<T", "Array<T")
}
for (arrayName in otherArrayNames) {
fun replace(it: String): String {
replaceGenerics(arrayName, it.replaceAll("<T> Iterable<T>", "${arrayName}Array").
replaceAll("<T> Iterable<T\\?>", "${arrayName}Array").
replaceAll("Iterable<T\\?>", "${arrayName}Array").
replaceAll("Iterable<T>", "${arrayName}Array"))
}
generateFile(File(outDir, "${arrayName}ArraysFromIterables.kt"), "package kotlin\n", File(srcDir, "Iterables.kt")) {
replace(it)
}
generateFile(File(outDir, "${arrayName}ArraysFromIterablesJVM.kt"), "package kotlin\n", File(srcDir, "IterablesJVM.kt")) {
replace(it)
}
generateFile(File(outDir, "${arrayName}ArraysFromIterablesLazy.kt"), "package kotlin\n", File(srcDir, "IterablesLazy.kt")) {
replace(it)
}
}
generateFile(File(outDir, "IteratorsFromIterables.kt"), "package kotlin", File(srcDir, "Iterables.kt")) {
it.replaceAll("Iterable<T", "Iterator<T")
}
generateFile(File(outDir, "IteratorsFromIterablesJVM.kt"), "package kotlin", File(srcDir, "IterablesJVM.kt")) {
it.replaceAll("Iterable<T", "Iterator<T")
}
// Collections - methods returning a collection of the same input size (if its a collection)
generateFile(File(outDir, "ArraysFromCollections.kt"), "package kotlin", File(srcDir, "Collections.kt")) {
it.replaceAll("Collection<T", "Array<T")
}
generateFile(File(outDir, "ArraysFromCollectionsJVM.kt"), "package kotlin", File(srcDir, "CollectionsJVM.kt")) {
it.replaceAll("Collection<T", "Array<T")
}
for (arrayName in otherArrayNames) {
generateFile(File(outDir, "${arrayName}ArraysFromCollections.kt"), "package kotlin", File(srcDir, "Collections.kt")) {
replaceGenerics(arrayName, it.replaceAll("<T> Collection<T>", "${arrayName}Array").
replaceAll("Collection<T>", "${arrayName}Array"))
}
generateFile(File(outDir, "${arrayName}ArraysFromCollectionsJVM.kt"), "package kotlin", File(srcDir, "CollectionsJVM.kt")) {
replaceGenerics(arrayName, it.replaceAll("<T> Collection<T>", "${arrayName}Array").
replaceAll("Collection<T>", "${arrayName}Array"))
}
}
generateFile(File(outDir, "StandardFromCollections.kt"), "package kotlin", File(srcDir, "Collections.kt")) {
it.replaceAll("Collection<T", "Iterable<T").replaceAll("(this.size)", "")
}
generateFile(File(outDir, "StandardFromCollectionsJVM.kt"), "package kotlin", File(srcDir, "CollectionsJVM.kt")) {
it.replaceAll("Collection<T", "Iterable<T").replaceAll("(this.size)", "")
}
generateDownTos(File(outDir, "DownTo.kt"), "package kotlin")
}
// Pretty hacky way to code generate; ideally we'd be using the AST and just changing the function prototypes
fun replaceGenerics(arrayName: String, it: String): String {
return it.replaceAll(" <in T>", " ").replaceAll("<in T, ", "<").replaceAll("<T, ", "<").replaceAll("<T,", "<").
replaceAll(" <T> ", " ").
replaceAll("<T>", "<${arrayName}>").replaceAll("<in T>", "<${arrayName}>").
replaceAll("\\(T\\)", "(${arrayName})").replaceAll("T\\?", "${arrayName}?").
replaceAll("T,", "${arrayName},").
replaceAll("T\\)", "${arrayName})").
replaceAll(" T ", " ${arrayName} ")
}