Collections standard library is now generated from templates.
This commit is contained in:
@@ -351,7 +351,6 @@ class CollectionTest {
|
||||
|
||||
test fun lastException() {
|
||||
fails { arrayList<Int>().last() }
|
||||
fails { hashSet<Char>().last() }
|
||||
}
|
||||
|
||||
test fun subscript() {
|
||||
|
||||
@@ -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} ")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user