Generate Kotlin/JS stdlib dependencies via dukat

This commit is contained in:
Alexey Trilis
2019-10-12 01:39:25 +02:00
committed by Shagen Ogandzhanian
parent 83bb07e5ac
commit b30537de0e
63 changed files with 6806 additions and 6192 deletions
+26
View File
@@ -0,0 +1,26 @@
plugins {
kotlin("jvm")
}
repositories {
maven("https://kotlin.bintray.com/dukat")
}
dependencies {
implementation(kotlinStdlib())
implementation("org.jetbrains.dukat:dukat:0.0.20.1")
implementation("org.jsoup:jsoup:1.8.2")
}
task("downloadIDL", JavaExec::class) {
main = "org.jetbrains.kotlin.tools.dukat.DownloadKt"
classpath = sourceSets["main"].runtimeClasspath
dependsOn(":dukat:build")
}
task("generateStdlibFromIDL", JavaExec::class) {
main = "org.jetbrains.kotlin.tools.dukat.LaunchKt"
classpath = sourceSets["main"].runtimeClasspath
dependsOn(":dukat:build")
systemProperty("line.separator", "\n")
}
@@ -1,22 +1,10 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.idl2k.dl
package org.jetbrains.kotlin.tools.dukat
import org.jetbrains.idl2k.urls
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.parser.Tag
@@ -45,7 +33,7 @@ fun main(args: Array<String>) {
val pkg = e.value.first().second
File(dir, fileName).bufferedWriter().use { w ->
w.appendln("namespace $pkg;")
w.appendln("package $pkg;")
w.appendln()
w.appendln()
@@ -104,3 +92,40 @@ private fun extractIDLText(rawContent: String, out: Appendable) {
soup.select("code.idl-code").attachTo(out)
soup.select("spec-idl").attachTo(out)
}
private val urls = listOf(
"https://raw.githubusercontent.com/whatwg/html-mirror/master/source" to "org.w3c.dom",
"https://html.spec.whatwg.org/" to "org.w3c.dom",
"https://raw.githubusercontent.com/whatwg/dom/master/dom.html" to "org.w3c.dom",
"https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html" to "org.w3c.dom",
"https://www.w3.org/TR/animation-timing/" to "org.w3c.dom",
"https://www.w3.org/TR/geometry-1/" to "org.w3c.dom",
"https://www.w3.org/TR/cssom-view/" to "org.w3c.dom",
"https://www.w3.org/TR/uievents/" to "org.w3c.dom.events",
"https://www.w3.org/TR/pointerevents/" to "org.w3c.dom.pointerevents",
"https://drafts.csswg.org/cssom/" to "org.w3c.dom.css",
"https://www.w3.org/TR/css-masking-1/" to "org.w3c.css.masking",
"https://w3c.github.io/mediacapture-main/" to "org.w3c.dom.mediacapture",
"https://www.w3.org/TR/DOM-Parsing/" to "org.w3c.dom.parsing",
"https://w3c.github.io/clipboard-apis" to "org.w3c.dom.clipboard",
"https://raw.githubusercontent.com/whatwg/url/master/url.html" to "org.w3c.dom.url",
"https://www.w3.org/TR/SVG2/single-page.html" to "org.w3c.dom.svg",
"https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl" to "org.khronos.webgl",
"https://www.khronos.org/registry/typedarray/specs/latest/typedarray.idl" to "org.khronos.webgl",
"https://raw.githubusercontent.com/whatwg/xhr/master/Overview.src.html" to "org.w3c.xhr",
"https://raw.githubusercontent.com/whatwg/fetch/master/Overview.src.html" to "org.w3c.fetch",
"https://raw.githubusercontent.com/w3c/FileAPI/gh-pages/index.html" to "org.w3c.files",
"https://raw.githubusercontent.com/whatwg/notifications/master/notifications.html" to "org.w3c.notifications",
"https://raw.githubusercontent.com/whatwg/fullscreen/master/fullscreen.html" to "org.w3c.fullscreen",
"https://www.w3.org/TR/vibration/" to "org.w3c.vibration",
"https://www.w3.org/TR/hr-time/" to "org.w3c.performance",
"https://www.w3.org/TR/2012/REC-navigation-timing-20121217/" to "org.w3c.performance",
"https://w3c.github.io/ServiceWorker/" to "org.w3c.workers"
)
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.dukat
import org.xml.sax.InputSource
import java.io.File
import javax.xml.xpath.XPathFactory
private val LINE_SEPARATOR = System.lineSeparator()
private fun readCopyrightNoticeFromProfile(copyrightProfile: File): String {
val template = copyrightProfile.reader().use { reader ->
XPathFactory.newInstance().newXPath().evaluate(
"/component/copyright/option[@name='notice']/@value",
InputSource(reader)
)
}
val yearTemplate = "&#36;today.year"
val year = java.time.LocalDate.now().year.toString()
assert(yearTemplate in template)
return template.replace(yearTemplate, year).lines()
.joinToString("", prefix = "/*$LINE_SEPARATOR", postfix = " */$LINE_SEPARATOR") {
" * $it$LINE_SEPARATOR"
}
}
private fun getHeader(): String {
val copyrightNotice = readCopyrightNoticeFromProfile(
File("../../../.idea/copyright/apache.xml")
)
val note = "// NOTE: THIS FILE IS AUTO-GENERATED, DO NOT EDIT!$LINE_SEPARATOR" +
"// See github.com/kotlin/dukat for details$LINE_SEPARATOR"
return copyrightNotice + LINE_SEPARATOR + note + LINE_SEPARATOR
}
fun main() {
val input = "../../stdlib/js/idl/org.w3c.dom.idl"
val outputDirectory = "../../stdlib/js/src/org.w3c/"
org.jetbrains.dukat.cli.main("-d", outputDirectory, input)
for (file in File(outputDirectory).listFiles { name ->
name.extension == "kt"
}.orEmpty()) {
file.writeBytes((getHeader() + file.readText()).toByteArray())
}
}
-41
View File
@@ -1,41 +0,0 @@
buildscript {
ext.antlr4_version = '4.7.1'
}
apply plugin: "antlr"
apply plugin: "kotlin"
if (project.findProperty("idl2k.deploy")?.toBoolean()) {
configurePublishing(project)
}
project.sourceSets.main.antlr.srcDirs = ["src/main/antlr4"]
dependencies {
antlr "org.antlr:antlr4:$antlr4_version"
implementation "org.antlr:antlr4-runtime:$antlr4_version"
implementation kotlinStdlib()
implementation "org.jsoup:jsoup:1.8.2"
testImplementation "junit:junit:4.12"
}
sourceSets.main.kotlin.srcDirs += file("$buildDir/generated-src/antlr/main/")
generateGrammarSource {
arguments += ["-visitor", "-long-messages", "-package", "org.antlr.webidl"]
}
compileKotlin.dependsOn generateGrammarSource
compileTestKotlin.dependsOn generateTestGrammarSource
task downloadIDL(type: JavaExec) {
main = "org.jetbrains.idl2k.dl.DownloadKt"
classpath = sourceSets.main.runtimeClasspath
}
task idl2k(type: JavaExec) {
main = "org.jetbrains.idl2k.MainKt"
classpath = sourceSets.main.runtimeClasspath
}
@@ -1,595 +0,0 @@
/*
BSD License
Copyright (c) 2013, 2015 Rainer Schuster
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 Rainer Schuster 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
HOLDER 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.
Web IDL grammar derived from:
http://heycam.github.io/webidl/
Web IDL (Second Edition)
W3C Editor's Draft 13 November 2014
*/
grammar WebIDL;
// Note: Replaced keywords: const, default, enum, interface, null.
// Note: Added "wrapper" rule webIDL with EOF token.
webIDL
: namespace? definitions EOF
;
namespaceScope
: '*' | 'cpp' | 'java' | 'py' | 'perl' | 'rb' | 'cocoa' | 'csharp'
;
namespaceRest
: IDENTIFIER_WEBIDL ( '.' IDENTIFIER_WEBIDL )*
;
namespace
: 'namespace' namespaceScope? namespaceRest ';';
definitions
: extendedAttributeList definition definitions
| /* empty */
;
definition
: callbackOrInterface
| partial
| dictionary
| enum_
| typedef
| exception_
| const_
| module
| implementsStatement
;
module
: 'module' IDENTIFIER_WEBIDL '{' definitions '}' ';'
;
callbackOrInterface
: 'callback' callbackRestOrInterface
| interface_
;
exception_
: 'exception' IDENTIFIER_WEBIDL inheritance '{' dictionaryMembers '}' ';'
;
callbackRestOrInterface
: callbackRest
| interface_
;
interface_
: 'interface' IDENTIFIER_WEBIDL inheritance '{' interfaceMembers '}' ';'
;
partial
: 'partial' partialDefinition
;
partialDefinition
: partialInterface
| partialDictionary
;
partialInterface
: 'interface' IDENTIFIER_WEBIDL '{' interfaceMembers '}' ';'
;
interfaceMembers
: extendedAttributeList interfaceMember interfaceMembers
| /* empty */
;
interfaceMember
: const_
| operation
| serializer
| stringifier
| staticMember
| iterable
| readonlyMember
| readWriteAttribute
| readWriteMaplike
| readWriteSetlike
| typedef
;
dictionary
: 'dictionary' IDENTIFIER_WEBIDL inheritance '{' dictionaryMembers '}' ';'
;
dictionaryMembers
: extendedAttributeList dictionaryMember dictionaryMembers
| /* empty */
;
dictionaryMember
: required type IDENTIFIER_WEBIDL default_ ';'
;
required
: 'required'
| /* empty */
;
partialDictionary
: 'dictionary' IDENTIFIER_WEBIDL '{' dictionaryMembers '}' ';'
;
default_
: '=' defaultValue
| /* empty */
;
defaultValue
: constValue
| STRING_WEBIDL
| '[' ']'
;
inheritance
: ':' IDENTIFIER_WEBIDL ( ',' IDENTIFIER_WEBIDL )*
| /* empty */
;
enum_
: 'enum' IDENTIFIER_WEBIDL '{' enumValueList '}' ';'
;
enumValueList
: STRING_WEBIDL enumValueListComma
;
enumValueListComma
: ',' enumValueListString
| /* empty */
;
enumValueListString
: STRING_WEBIDL enumValueListComma
| /* empty */
;
callbackRest
: IDENTIFIER_WEBIDL '=' returnType '(' argumentList ')' ';'
;
typedef
: 'typedef' type IDENTIFIER_WEBIDL ';'
;
implementsStatement
: IDENTIFIER_WEBIDL 'implements' IDENTIFIER_WEBIDL ';'
;
const_
: 'const' constType IDENTIFIER_WEBIDL '=' constValue ';'
;
constValue
: booleanLiteral
| floatLiteral
| INTEGER_WEBIDL
| 'null'
;
booleanLiteral
: 'true'
| 'false'
;
floatLiteral
: FLOAT_WEBIDL
| '-Infinity'
| 'Infinity'
| 'NaN'
;
serializer
: 'serializer' serializerRest
;
serializerRest
: operationRest
| '=' serializationPattern
| /* empty */
;
serializationPattern
: '{' serializationPatternMap '}'
| '[' serializationPatternList ']'
| IDENTIFIER_WEBIDL
;
serializationPatternMap
: 'getter'
| 'inherit' identifiers
| IDENTIFIER_WEBIDL identifiers
| /* empty */
;
serializationPatternList
: 'getter'
| IDENTIFIER_WEBIDL identifiers
| /* empty */
;
stringifier
: 'stringifier' stringifierRest
;
stringifierRest
: readOnly attributeRest
| returnType operationRest
| ';'
;
staticMember
: 'static' staticMemberRest
;
staticMemberRest
: readOnly attributeRest
| returnType operationRest
;
readonlyMember
: 'readonly' readonlyMemberRest
;
readonlyMemberRest
: attributeRest
| maplikeRest
| setlikeRest
;
readWriteAttribute
: 'inherit' readOnly attributeRest
| attributeRest
;
attributeRest
: 'attribute' type (IDENTIFIER_WEBIDL | 'required') attributeAnnotations* ';'
;
attributeAnnotations
: IDENTIFIER_WEBIDL ( '(' type (',' type)* ')' )?
;
inherit
: 'inherit'
| /* empty */
;
readOnly
: 'readonly'
| /* empty */
;
operation
: returnType operationRest
| specialOperation
;
specialOperation
: special specials returnType operationRest
;
specials
: special specials
| /* empty */
;
special
: 'getter'
| 'setter'
| 'creator'
| 'deleter'
| 'legacycaller'
;
operationRest
: optionalIdentifier '(' argumentList ')' attributeAnnotations* ';'
;
optionalIdentifier
: IDENTIFIER_WEBIDL
| /* empty */
;
argumentList
: argument arguments
| /* empty */
;
arguments
: ',' argument arguments
| /* empty */
;
argument
: extendedAttributeList optionalOrRequiredArgument
;
optionalOrRequiredArgument
: 'optional'? ('in'|'out')? type ellipsis argumentName default_
;
argumentName
: argumentNameKeyword
| IDENTIFIER_WEBIDL
;
ellipsis
: '...'
| /* empty */
;
iterable
: 'iterable' '<' type optionalType '>' ';'
| 'legacyiterable' '<' type '>' ';'
;
optionalType
: ',' type
| /* empty */
;
readWriteMaplike
: maplikeRest
;
readWriteSetlike
: setlikeRest
;
maplikeRest
: 'maplike' '<' type ',' type '>' ';'
;
setlikeRest
: 'setlike' '<' type '>' ';'
;
extendedAttributeList
: '[' extendedAttribute extendedAttributes ']'
| /* empty */
;
extendedAttributes
: ',' extendedAttribute extendedAttributes
| /* empty */
;
extendedAttribute
: extendedAttributeNamePart? IDENTIFIER_WEBIDL ('(' argumentList ')')?
| extendedAttributeNamePart? IDENTIFIER_WEBIDL? '(' argumentList ')'
| extendedAttributeNamePart? IDENTIFIER_WEBIDL? '(' identifierList ')'
| /* empty */
;
extendedAttributeNamePart
: IDENTIFIER_WEBIDL '='
;
argumentNameKeyword
: 'attribute'
| 'callback'
| 'const'
| 'creator'
| 'deleter'
| 'dictionary'
| 'enum'
| 'getter'
| 'implements'
| 'inherit'
| 'interface'
| 'iterable'
| 'legacycaller'
| 'legacyiterable'
| 'maplike'
| 'partial'
| 'required'
| 'serializer'
| 'setlike'
| 'setter'
| 'static'
| 'stringifier'
| 'typedef'
| 'unrestricted'
| 'namespace'
;
type
: singleType
| unionType typeSuffix
;
singleType
: nonAnyType
| 'any' typeSuffixStartingWithArray
;
unionType
: '(' unionMemberType ( 'or' unionMemberType )* ')'
;
unionMemberType
: nonAnyType
| unionType typeSuffix
| 'any' '[' ']' typeSuffix
;
unionMemberTypes
: 'or' unionMemberType unionMemberTypes
| /* empty */
;
nonAnyType
: primitiveType typeSuffix
| promiseType null_
| 'ByteString' typeSuffix
| 'DOMString' typeSuffix
| 'USVString' typeSuffix
| IDENTIFIER_WEBIDL typeSuffix
| sequenceType null_
| 'object' typeSuffix
| 'Date' typeSuffix
| 'RegExp' typeSuffix
| 'DOMException' typeSuffix
| IDENTIFIER_WEBIDL '<' type '>' null_
;
constType
: primitiveType null_
| IDENTIFIER_WEBIDL null_
;
primitiveType
: unsignedIntegerType
| unrestrictedFloatType
| 'boolean'
| 'byte'
| 'octet'
;
unrestrictedFloatType
: 'unrestricted' floatType
| floatType
;
floatType
: 'float'
| 'double'
;
unsignedIntegerType
: 'unsigned' integerType
| integerType
;
integerType
: 'short'
| 'long'+
;
sequenceType
: 'sequence' '<' type '>'
| 'FrozenArray' '<' type '>'
;
promiseType
: 'Promise' '<' returnType '>'
;
typeSuffix
: '[' ']' typeSuffix
| '?' typeSuffixStartingWithArray
| /* empty */
;
typeSuffixStartingWithArray
: '[' ']' typeSuffix
| /* empty */
;
null_
: '?'
| /* empty */
;
returnType
: type
| 'void'
;
identifierList
: IDENTIFIER_WEBIDL identifiers
;
identifiers
: ',' IDENTIFIER_WEBIDL identifiers
| /* empty */
;
extendedAttributeNoArgs
: IDENTIFIER_WEBIDL
;
extendedAttributeArgList
: IDENTIFIER_WEBIDL '(' argumentList ')'
;
extendedAttributeIdent
: IDENTIFIER_WEBIDL '=' IDENTIFIER_WEBIDL
;
extendedAttributeIdentList
: IDENTIFIER_WEBIDL '=' '(' identifierList ')'
;
extendedAttributeNamedArgList
: IDENTIFIER_WEBIDL '=' IDENTIFIER_WEBIDL '(' argumentList ')'
;
INTEGER_WEBIDL
: '-'?('0'([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)
;
FLOAT_WEBIDL
: '-'?(([0-9]+'.'[0-9]*|[0-9]*'.'[0-9]+)([Ee][+\-]?[0-9]+)?|[0-9]+[Ee][+\-]?[0-9]+)
;
IDENTIFIER_WEBIDL
: [A-Z_a-z][0-9A-Z_a-z]*
;
STRING_WEBIDL
: '"' ~["]* '"'
;
WHITESPACE_WEBIDL
: [\t\n\r ]+ -> channel(HIDDEN)
;
COMMENT_WEBIDL
: ('//'~[\n\r]*|'/*'(.|'\n')*?'*/')+ -> channel(HIDDEN)
; // Note: '/''/'~[\n\r]* instead of '/''/'.* (non-greedy because of wildcard).
OTHER_WEBIDL
: ~[\t\n\r 0-9A-Z_a-z]
;
@@ -1,124 +0,0 @@
package org.jetbrains.idl2k
import org.antlr.v4.runtime.CharStreams
import java.io.File
import java.io.IOException
import java.net.URL
import java.util.ArrayList
import java.util.LinkedHashMap
class BuildWebIdl(val mdnCacheFile: File, val srcDir: File) {
val repositoryPre = loadPreliminaryRepository()
fun loadPreliminaryRepository(): Repository {
if (!srcDir.exists()) {
System.err?.println("Directory ${srcDir.absolutePath} doesn't exist")
System.exit(1)
}
return srcDir.walkTopDown().filter { it.isDirectory || it.extension == "idl" }.asSequence().filter { it.isFile }.toList()
.sortedBy { it.absolutePath }.fold(Repository(emptyMap(), emptyMap(), emptyMap(), emptyMap())) { acc, e ->
System.err.flush()
System.err.println("Parsing ${e.absolutePath}")
val fileRepository = parseIDL(CharStreams.fromFileName(e.absolutePath, Charsets.UTF_8))
Repository(
interfaces = acc.interfaces.mergeReduce(fileRepository.interfaces, ::merge),
typeDefs = acc.typeDefs + fileRepository.typeDefs,
externals = acc.externals.merge(fileRepository.externals),
enums = acc.enums + fileRepository.enums
)
}
}
init {
println("Prepare...")
}
val repository =
repositoryPre.copy(typeDefs = repositoryPre.typeDefs.mapValues { it.value.copy(mapType(repositoryPre, it.value.types)) })
val definitions = implementInterfaces(mapDefinitions(repository, repository.interfaces.values).map {
if (it.name in relocations) {
// we need this to get interfaces listed in the relocations in valid package
// to keep compatibility with DOM Java API
it.copy(namespace = relocations[it.name]!!)
} else {
it
}
})
val unions = generateUnions(definitions, repository.typeDefs.values)
val allPackages = (definitions.asSequence().map { it.namespace } + repository.enums.values.map { it.namespace }).distinct().sorted()
val mdnCache by lazy { updateMdnCache() }
fun updateMdnCache(): MDNDocumentationCache {
println("Processing MDN")
val oldMdnCache = if (mdnCacheFile.canRead()) MDNDocumentationCache.read(mdnCacheFile) else MDNDocumentationCache.Empty
val newMdnCacheExisting = HashSet(oldMdnCache.existing)
val newMdnCacheNonExisting = HashSet(oldMdnCache.nonExisting)
for (iface in definitions) {
val url = "https://developer.mozilla.org/en/docs/Web/API/${iface.name}"
val addUrl = when (oldMdnCache.checkInCache(url)) {
true -> true
false -> false
else -> try {
val text = URL(url).openStream().reader().use { it.readText() }
text.contains(iface.name, ignoreCase = true)
} catch (ignore: IOException) {
false
}
}
if (addUrl)
newMdnCacheExisting.add(url)
else
newMdnCacheNonExisting.add(url)
}
val mdnCache = MDNDocumentationCache(newMdnCacheExisting, newMdnCacheNonExisting)
MDNDocumentationCache.writeTo(mdnCache, mdnCacheFile)
return mdnCache
}
}
internal fun <K, V> Map<K, List<V>>.reduceValues(reduce: (V, V) -> V = { _, b -> b }): Map<K, V> = mapValues { it.value.reduce(reduce) }
internal fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V = { _, b -> b }): Map<K, V> {
val result = LinkedHashMap<K, V>(this.size + other.size)
result.putAll(this)
other.forEach { e ->
val existing = result[e.key]
if (existing == null) {
result[e.key] = e.value
} else {
result[e.key] = reduce(e.value, existing)
}
}
return result
}
internal fun <K, V> Map<K, List<V>>.merge(other: Map<K, List<V>>): Map<K, List<V>> {
val result = LinkedHashMap<K, MutableList<V>>(size + other.size)
this.forEach {
result[it.key] = ArrayList(it.value)
}
other.forEach {
val list = result[it.key]
if (list == null) {
result[it.key] = ArrayList(it.value)
} else {
list.addAll(it.value)
}
}
return result
}
@@ -1,185 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.idl2k
val urls = listOf(
"https://raw.githubusercontent.com/whatwg/html-mirror/master/source" to "org.w3c.dom",
"https://html.spec.whatwg.org/" to "org.w3c.dom",
"https://raw.githubusercontent.com/whatwg/dom/master/dom.html" to "org.w3c.dom",
"https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html" to "org.w3c.dom",
"https://www.w3.org/TR/animation-timing/" to "org.w3c.dom",
"https://www.w3.org/TR/geometry-1/" to "org.w3c.dom",
"https://www.w3.org/TR/cssom-view/" to "org.w3c.dom",
"https://www.w3.org/TR/uievents/" to "org.w3c.dom.events",
"https://www.w3.org/TR/pointerevents/" to "org.w3c.dom.pointerevents",
"https://drafts.csswg.org/cssom/" to "org.w3c.dom.css",
"https://www.w3.org/TR/css-masking-1/" to "org.w3c.css.masking",
"https://w3c.github.io/mediacapture-main/" to "org.w3c.dom.mediacapture",
"https://www.w3.org/TR/DOM-Parsing/" to "org.w3c.dom.parsing",
"https://w3c.github.io/clipboard-apis" to "org.w3c.dom.clipboard",
"https://raw.githubusercontent.com/whatwg/url/master/url.html" to "org.w3c.dom.url",
"https://www.w3.org/TR/SVG2/single-page.html" to "org.w3c.dom.svg",
"https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl" to "org.khronos.webgl",
"https://www.khronos.org/registry/typedarray/specs/latest/typedarray.idl" to "org.khronos.webgl",
"https://raw.githubusercontent.com/whatwg/xhr/master/Overview.src.html" to "org.w3c.xhr",
"https://raw.githubusercontent.com/whatwg/fetch/master/Overview.src.html" to "org.w3c.fetch",
"https://raw.githubusercontent.com/w3c/FileAPI/gh-pages/index.html" to "org.w3c.files",
"https://raw.githubusercontent.com/whatwg/notifications/master/notifications.html" to "org.w3c.notifications",
"https://raw.githubusercontent.com/whatwg/fullscreen/master/fullscreen.html" to "org.w3c.fullscreen",
"https://www.w3.org/TR/vibration/" to "org.w3c.vibration",
"https://www.w3.org/TR/hr-time/" to "org.w3c.performance",
"https://www.w3.org/TR/2012/REC-navigation-timing-20121217/" to "org.w3c.performance",
"https://w3c.github.io/ServiceWorker/" to "org.w3c.workers"
)
val relocations = mapOf(
"Event" to "org.w3c.dom.events",
"EventTarget" to "org.w3c.dom.events",
"EventListener" to "org.w3c.dom.events"
)
val commentOutDeclarations = setOf(
"MouseEvent.screenX: Double", "MouseEvent.screenY: Double",
"MouseEvent.clientX: Double", "MouseEvent.clientY: Double",
"MouseEvent.x: Double", "MouseEvent.y: Double",
"HTMLAllCollection.namedItem",
"HTMLAllCollection.get",
"HTMLFormControlsCollection.namedItem",
"HTMLFormControlsCollection.get",
"HTMLPropertiesCollection.namedItem",
"HTMLPropertiesCollection.get",
"SVGElement.id"
)
val requiredArguments = setOf(
"DOMPoint.constructor.point",
"DOMQuad.constructor.rect"
)
val inheritanceExclude = mapOf(
"SVGAElement" to setOf("HTMLHyperlinkElementUtils")
)
val kotlinBuiltinInterfaces = mapOf(
"ItemArrayLike" to GenerateClass("ItemArrayLike", "org.w3c.dom", GenerateDefinitionKind.INTERFACE, emptyList(),
memberAttributes = mutableListOf(GenerateAttribute("length", SimpleType("Int", false), null, false, AttributeKind.VAL, false, false, false, false)),
memberFunctions = mutableListOf(GenerateFunction("item", DynamicType, listOf(
GenerateAttribute("index", SimpleType("Int", false), null, false, AttributeKind.ARGUMENT, false, false, false, false)
), NativeGetterOrSetter.NONE, false, false)),
constants = emptyList(),
generateBuilderFunction = false,
primaryConstructor = null,
secondaryConstructors = emptyList()
)
)
val eventSpecifierMapper = mapOf<String, String>(
"onbeforeunload" to "BeforeUnloadEvent",
"ondrag" to "DragEvent",
"ondragend" to "DragEvent",
"ondragenter" to "DragEvent",
"ondragexit" to "DragEvent",
"ondragleave" to "DragEvent",
"ondragover" to "DragEvent",
"ondragstart" to "DragEvent",
"ondrop" to "DragEvent",
"oncopy" to "ClipboardEvent",
"oncut" to "ClipboardEvent",
"onpaste" to "ClipboardEvent",
"onfetch" to "FetchEvent",
"onblur" to "FocusEvent",
"onfocus" to "FocusEvent",
"onhashchange" to "HashChangeEvent",
"oninput" to "InputEvent",
"onkeydown" to "KeyboardEvent",
"onkeypress" to "KeyboardEvent",
"onkeyup" to "KeyboardEvent",
"onmessage" to "MessageEvent",
"onclick" to "MouseEvent",
"oncontextmenu" to "MouseEvent",
"ondblclick" to "MouseEvent",
"onmousedown" to "MouseEvent",
"onmouseenter" to "MouseEvent",
"onmouseleave" to "MouseEvent",
"onmousemove" to "MouseEvent",
"onmouseout" to "MouseEvent",
"onmouseover" to "MouseEvent",
"onmouseup" to "MouseEvent",
"onnotificationclick" to "NotificationEvent",
"onnotificationclose" to "NotificationEvent",
"onpagehide" to "PageTransitionEvent",
"onpageshow" to "PageTransitionEvent",
"ongotpointercapture" to "PointerEvent",
"onlostpointercapture" to "PointerEvent",
"onpointercancel" to "PointerEvent",
"onpointerdown" to "PointerEvent",
"onpointerenter" to "PointerEvent",
"onpointerleave" to "PointerEvent",
"onpointermove" to "PointerEvent",
"onpointerout" to "PointerEvent",
"onpointerover" to "PointerEvent",
"onpointerup" to "PointerEvent",
"onpopstate" to "PopStateEvent",
"onloadstart" to "ProgressEvent",
"onprogress" to "ProgressEvent",
"onunhandledrejection" to "PromiseRejectionEvent",
"onstorage" to "StorageEvent",
"onwheel" to "WheelEvent"
)
data class EventMapKey(val name: String, val context: String)
val eventSpecifierMapperWithContext = mapOf<EventMapKey, String>(
EventMapKey("onaddtrack", "MediaStream") to "MediaStreamTrackEvent",
EventMapKey("onremovetrack", "MediaStream") to "MediaStreamTrackEvent",
EventMapKey("onaddtrack", "AudioTrackList") to "TrackEvent",
EventMapKey("onaddtrack", "TextTrackList") to "TrackEvent",
EventMapKey("onaddtrack", "VideoTrackList") to "TrackEvent",
EventMapKey("onremovetrack", "AudioTrackList") to "TrackEvent",
EventMapKey("onremovetrack", "TextTrackList") to "TrackEvent",
EventMapKey("onremovetrack", "VideoTrackList") to "TrackEvent"
)
@@ -1,368 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* 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 org.jetbrains.idl2k
import org.jetbrains.idl2k.util.mapEnumConstant
private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet().let { attributes ->
when {
"getter" in attributes -> NativeGetterOrSetter.GETTER
"setter" in attributes -> NativeGetterOrSetter.SETTER
else -> NativeGetterOrSetter.NONE
}
}
fun generateFunction(repository: Repository, function: Operation, functionName: String, nativeGetterOrSetter: NativeGetterOrSetter = function.getterOrSetter()): GenerateFunction =
function.attributes.map { it.call }.toSet().let {
GenerateFunction(
name = functionName,
returnType = mapType(repository, function.returnType).let { mapped -> if (nativeGetterOrSetter == NativeGetterOrSetter.GETTER) mapped.toNullableIfNonPrimitive() else mapped },
arguments = function.parameters.map {
val mappedType = mapType(repository, it.type)
GenerateAttribute(
name = it.name,
type = mappedType,
initializer = if (it.defaultValue != null) "definedExternally" else null,
getterSetterNoImpl = false,
override = false,
kind = AttributeKind.ARGUMENT,
vararg = it.vararg,
static = it.static,
required = it.required
)
},
nativeGetterOrSetter = nativeGetterOrSetter,
static = function.static,
override = false
)
}
fun generateFunctions(repository: Repository, function: Operation): List<GenerateFunction> {
val realFunction = when {
function.name == "" -> null
function.getterOrSetter() == NativeGetterOrSetter.NONE -> generateFunction(repository, function, function.name, NativeGetterOrSetter.NONE)
function.name == "get" || function.name == "set" -> null
else -> generateFunction(repository, function, function.name, NativeGetterOrSetter.NONE)
}
val getterOrSetterFunction = when (function.getterOrSetter()) {
NativeGetterOrSetter.NONE -> null
NativeGetterOrSetter.GETTER -> generateFunction(repository, function, "get")
NativeGetterOrSetter.SETTER -> generateFunction(repository, function, "set")
}
val callbackArgumentsAsLambdas = function.parameters.map {
val parameterType = mapType(repository, it.type) as? SimpleType
val interfaceType = repository.interfaces[parameterType?.type]
when {
interfaceType == null -> it
interfaceType.operations.size != 1 -> it
interfaceType.callback -> interfaceType.operations.single().let { callbackFunction ->
it.copy(type = FunctionType(callbackFunction.parameters.map { it.copy(type = mapType(repository, it.type)) }, mapType(repository, callbackFunction.returnType), parameterType?.nullable ?: false))
}
else -> it
}
}
val functionWithCallbackOrNull = when {
callbackArgumentsAsLambdas == function.parameters -> null
realFunction != null -> generateFunction(repository, function.copy(parameters = callbackArgumentsAsLambdas), function.name, NativeGetterOrSetter.NONE)
else -> null
}
return listOf(realFunction, getterOrSetterFunction, functionWithCallbackOrNull).filterNotNull()
}
fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Attribute, nullableAttributes: Boolean): GenerateAttribute {
val mappedType = mapType(repository, attribute.type).let { if (nullableAttributes) it.toNullable() else it }
return GenerateAttribute(attribute.name,
type = mappedType,
initializer =
if (putNoImpl && !attribute.static) {
mapLiteral(attribute.defaultValue, mapType(repository, attribute.type), repository.enums)
} else if (attribute.defaultValue != null) {
"definedExternally"
} else {
null
},
getterSetterNoImpl = putNoImpl,
kind = if (attribute.readOnly) AttributeKind.VAL else AttributeKind.VAR,
override = false,
vararg = attribute.vararg,
static = attribute.static,
required = attribute.required
)
}
private fun InterfaceDefinition.isInterface() : Boolean {
return when {
dictionary -> true
extendedAttributes.any { it.call == "NoInterfaceObject" } -> true
callback -> true
else -> false
}
}
private fun InterfaceDefinition.superTypes(repository: Repository) = superTypes.map { repository.interfaces[it] }.filterNotNull()
private fun resolveDefinitionKind(repository: Repository, iface: InterfaceDefinition, constructors: List<ExtendedAttribute> = iface.findConstructors()): GenerateDefinitionKind =
when {
iface.isInterface() -> GenerateDefinitionKind.INTERFACE
constructors.isNotEmpty() || iface.superTypes(repository).any { resolveDefinitionKind(repository, it) == GenerateDefinitionKind.CLASS } -> {
GenerateDefinitionKind.CLASS
}
else -> GenerateDefinitionKind.ABSTRACT_CLASS
}
private fun InterfaceDefinition.mapAttributes(repository: Repository)
= attributes.map { generateAttribute(putNoImpl = dictionary, repository = repository, attribute = it, nullableAttributes = dictionary) }
private fun InterfaceDefinition.mapOperations(repository: Repository) = operations.flatMap { generateFunctions(repository, it) }
private fun Constant.mapConstant(repository : Repository) = GenerateAttribute(name, mapType(repository, type), null, false, AttributeKind.VAL, false, false, true, false)
private val EMPTY_CONSTRUCTOR = ExtendedAttribute(null, "Constructor", emptyList())
fun generateTrait(repository: Repository, iface: InterfaceDefinition): List<GenerateClass> {
val superClasses = iface.superTypes
.mapNotNull { repository.interfaces[it] }
.filter {
when (resolveDefinitionKind(repository, it)) {
GenerateDefinitionKind.CLASS,
GenerateDefinitionKind.ABSTRACT_CLASS -> true
else -> false
}
}
assert(superClasses.size <= 1) { "Type ${iface.name} should have one or zero super classes but found ${superClasses.map { it.name }}" }
val declaredConstructors = iface.findConstructors()
val entityKind = resolveDefinitionKind(repository, iface, declaredConstructors)
val extensions = repository.externals[iface.name]?.mapNotNull { repository.interfaces[it] } ?: emptyList()
val primaryConstructor = when {
declaredConstructors.size == 1 -> declaredConstructors.single()
declaredConstructors.isEmpty() && (entityKind == GenerateDefinitionKind.CLASS || entityKind == GenerateDefinitionKind.ABSTRACT_CLASS) -> EMPTY_CONSTRUCTOR
else -> declaredConstructors.firstOrNull { it.arguments.isEmpty() }
}
val secondaryConstructors = declaredConstructors.filter { it != primaryConstructor }
val primaryConstructorWithCall = primaryConstructor?.let { constructor ->
val constructorAsFunction = generateConstructorAsFunction(repository, constructor)
ConstructorWithSuperTypeCall(constructorAsFunction, constructor)
}
val secondaryConstructorsWithCall = secondaryConstructors.map { secondaryConstructor ->
val constructorAsFunction = generateConstructorAsFunction(repository, secondaryConstructor)
ConstructorWithSuperTypeCall(constructorAsFunction, secondaryConstructor)
}
val memberAttributes = iface.mapAttributes(repository)
val memberFunctions = iface.mapOperations(repository)
val extensionsNames = extensions.map { it.name }
val namedConstructors =
iface.findExtendedAttributes("NamedConstructor").map { namedConstructor ->
GenerateClass(
name = namedConstructor.call,
namespace = iface.namespace,
kind = GenerateDefinitionKind.CLASS,
superTypes = listOf(iface.name) + extensionsNames,
memberAttributes = memberAttributes.toMutableList(),
memberFunctions = memberFunctions.toMutableList(),
constants = emptyList(),
primaryConstructor = ConstructorWithSuperTypeCall(generateConstructorAsFunction(repository, namedConstructor), namedConstructor),
secondaryConstructors = emptyList(),
generateBuilderFunction = false
)
}
return (listOf(GenerateClass(iface.name, iface.namespace, entityKind, (iface.superTypes + extensionsNames).distinct(),
memberAttributes = memberAttributes.toMutableList(),
memberFunctions = memberFunctions.toMutableList(),
constants = (iface.constants.map { it.mapConstant(repository) } + extensions.flatMap { it.constants.map { it.mapConstant(repository) } }.distinct().toList()),
primaryConstructor = primaryConstructorWithCall,
secondaryConstructors = secondaryConstructorsWithCall,
generateBuilderFunction = iface.dictionary
)) + namedConstructors).map(::markAsArrayLikeIfApplicable)
}
fun markAsArrayLikeIfApplicable(iface: GenerateClass): GenerateClass {
fun isInt(type: Type) = type is SimpleType && type.type == "Int"
val lengthProperty = iface.memberAttributes.singleOrNull { it.name == "length" && isInt(it.type) }
val itemAccessFunction = iface.memberFunctions.singleOrNull { it.name == "item" && it.arguments.map { isInt(it.type) } == listOf(true) && it.returnType != UnitType }
if (lengthProperty == null || itemAccessFunction == null) return iface
return iface.copy(superTypes = iface.superTypes + "ItemArrayLike<${itemAccessFunction.returnType.dropNullable().render()}>")
}
fun generateConstructorAsFunction(repository: Repository, constructor: ExtendedAttribute) = generateFunction(
repository,
Operation("constructor", UnitType, constructor.arguments, emptyList(), false),
functionName = "constructor",
nativeGetterOrSetter = NativeGetterOrSetter.NONE)
fun mapUnionType(it: UnionType) = GenerateClass(
name = it.name,
namespace = it.namespace,
kind = GenerateDefinitionKind.INTERFACE,
superTypes = emptyList(),
memberAttributes = mutableListOf(),
memberFunctions = mutableListOf(),
constants = emptyList(),
primaryConstructor = null,
secondaryConstructors = emptyList(),
generateBuilderFunction = false
)
fun generateUnionTypeTraits(allUnionTypes: Sequence<UnionType>): Sequence<GenerateClass> = allUnionTypes.map(::mapUnionType)
fun mapDefinitions(repository: Repository, definitions: Iterable<InterfaceDefinition>) =
definitions.flatMap { generateTrait(repository, it) }
fun generateUnions(ifaces: List<GenerateClass>, typedefs: Iterable<TypedefDefinition>): GenerateUnionTypes {
val declaredTypes = ifaces.associateBy { it.name }
val anonymousUnionTypes = collectUnionTypes(declaredTypes)
val anonymousUnionTypeTraits = generateUnionTypeTraits(anonymousUnionTypes)
val anonymousUnionsMap = anonymousUnionTypeTraits.associateBy { it.name }
val typedefsToBeGenerated = typedefs.filter { it.types is UnionType }
.map { NamedValue(it.name, it.types as UnionType) }
.filter { it.value.memberTypes.all { type -> type is SimpleType && type.type in declaredTypes } }
val typedefsMarkersMap = typedefsToBeGenerated.groupBy { it.name }.mapValues { mapUnionType(it.value.first().value).copy(name = it.key) }
val typeNamesToUnions = anonymousUnionTypes
.toList()
.flatMap { unionType ->
unionType.memberTypes
.filterIsInstance<SimpleType>()
.map { unionMember -> unionMember.type to unionType.name }
}.toMultiMap()
.merge(typedefsToBeGenerated
.flatMap { typedef ->
typedef.value.memberTypes
.filterIsInstance<SimpleType>()
.map { unionMember -> unionMember.type to typedef.name }
}.toMultiMap())
return GenerateUnionTypes(
typeNamesToUnionsMap = typeNamesToUnions,
anonymousUnionsMap = anonymousUnionsMap,
typedefsMarkersMap = typedefsMarkersMap
)
}
private fun mapLiteral(literal: String?, expectedType: Type = DynamicType, enums: Map<String, EnumDefinition>) =
if (literal != null && expectedType is SimpleType && expectedType.type in enums.keys) {
expectedType.type + "." + mapEnumConstant(literal.removeSurrounding("\"", "\""))
}
else {
when (literal) {
"[]" -> when {
expectedType == DynamicType -> "arrayOf<dynamic>()"
expectedType is AnyType -> "arrayOf<dynamic>()"
expectedType is UnionType -> "arrayOf<dynamic>()"
else -> "arrayOf()"
}
else -> literal
}
}
private fun specifyType(name: String, type: Type, context: String): Type {
if ((type is SimpleType) && (type.type == "Event")) {
(eventSpecifierMapper[name] ?: eventSpecifierMapperWithContext[EventMapKey(name, context)])?.let {
return type.copy(type = it)
}
}
return type
}
private fun generalizeType(name: String, type: Type, context: String): Type {
if ((type is FunctionType) && (type.parameterTypes.size == 1)) {
val paramAttribute = type.parameterTypes[0]
return type.copy(parameterTypes = listOf(paramAttribute.copy(type = specifyType(name, paramAttribute.type, context))))
}
return type
}
fun implementInterfaces(declarations: List<GenerateClass>) : List<GenerateClass> {
val unimplementedMemberMap = getUnimplementedMembers(declarations)
val nonAbstractDeclarations = declarations.filter { it.kind == GenerateDefinitionKind.CLASS }
for (declaration in nonAbstractDeclarations) {
val unimplementedMembers = unimplementedMemberMap[declaration.name] ?: continue
for (attribute in unimplementedMembers.attributes) {
declaration.memberAttributes += attribute.copy(override = true)
}
for (function in unimplementedMembers.functions) {
declaration.memberFunctions += function.copy(override = true)
}
}
return declarations.map { declaration ->
declaration.copy(
memberAttributes = declaration
.memberAttributes.map { attribute -> attribute.copy(type = generalizeType(attribute.name, attribute.type, declaration.name)) }.toMutableList()
)
}
}
private fun getUnimplementedMembers(declarations: List<GenerateClass>): Map<String, UnimplementedMembers> {
val declarationMap = declarations.associate { it.name to it }
val unimplementedMemberCache = mutableMapOf<String, UnimplementedMembers>()
fun getForClass(className: String): UnimplementedMembers = unimplementedMemberCache.getOrPut(className) {
val declaration = declarationMap[className] ?: return@getOrPut UnimplementedMembers(emptyList(), emptyList())
val unimplementedInSuperClasses = declaration.superTypes.map { getForClass(it) }
val attributeMap = unimplementedInSuperClasses
.flatMap { it.attributes }
.associate { it.name to it }
.toMutableMap()
val functionMap = unimplementedInSuperClasses
.flatMap { it.functions }
.associate { "${it.name}(${it.signature})" to it }
.toMutableMap()
val (implementedAttributes, unimplementedAttributes) = declaration.memberAttributes
.filter { !it.static }
.partition { declaration.kind != GenerateDefinitionKind.INTERFACE && !it.getterSetterNoImpl }
val (implementedFunctions, unimplementedFunctions) = declaration.memberFunctions
.filter { !it.static }
.partition { declaration.kind != GenerateDefinitionKind.INTERFACE }
attributeMap += unimplementedAttributes.map { it.name to it }
attributeMap.keys -= implementedAttributes.map { it.name }
functionMap += unimplementedFunctions.map { "${it.name}(${it.signature})" to it }
functionMap.keys -= implementedFunctions.map { "${it.name}(${it.signature})" }
UnimplementedMembers(attributeMap.values.toList(), functionMap.values.toList())
}
for (declaration in declarations) {
getForClass(declaration.name)
}
return unimplementedMemberCache
}
private class UnimplementedMembers(val attributes: List<GenerateAttribute>, val functions: List<GenerateFunction>)
@@ -1,31 +0,0 @@
package org.jetbrains.idl2k
import java.io.File
fun BuildWebIdl.jsGenerator(outDir: File, copyrightNotice: String) {
outDir.deleteRecursively()
outDir.mkdirs()
allPackages.forEach { pkg ->
File(outDir, pkg + ".kt").bufferedWriter().use { w ->
println("Generating for package $pkg...")
w.appendln(copyrightNotice)
w.appendln("// NOTE: THIS FILE IS AUTO-GENERATED, DO NOT EDIT!")
w.appendln("// See libraries/tools/idl2k for details")
w.appendln()
w.appendln("@file:Suppress(\"NESTED_CLASS_IN_EXTERNAL_INTERFACE\")")
w.appendln("package $pkg")
w.appendln()
w.appendln("import kotlin.js.*")
allPackages.filter { it != pkg }.forEach { import ->
w.appendln("import $import.*")
}
w.appendln()
w.render(pkg, definitions, unions, repository.enums.values.toList(), mdnCache)
}
}
}
@@ -1,612 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.idl2k
import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.TerminalNode
import org.antlr.webidl.WebIDLBaseVisitor
import org.antlr.webidl.WebIDLLexer
import org.antlr.webidl.WebIDLParser
import org.antlr.webidl.WebIDLParser.*
import java.util.*
data class ExtendedAttribute(val name: String?, val call: String, val arguments: List<Attribute>)
data class Operation(val name: String, val returnType: Type, val parameters: List<Attribute>, val attributes: List<ExtendedAttribute>, val static: Boolean)
data class Attribute(val name: String, val type: Type, val readOnly: Boolean = true, val defaultValue: String? = null, val vararg: Boolean, val static: Boolean, val required: Boolean)
data class Constant(val name: String, val type: Type, val value: String?)
enum class DefinitionKind {
INTERFACE,
TYPEDEF,
EXTENSION_INTERFACE,
ENUM,
DICTIONARY
}
interface Definition
data class TypedefDefinition(val types: Type, val namespace: String, val name: String) : Definition
data class InterfaceDefinition(
val name: String,
val namespace: String,
val extendedAttributes: List<ExtendedAttribute>,
val operations: List<Operation>,
val attributes: List<Attribute>,
val superTypes: List<String>,
val constants: List<Constant>,
val dictionary: Boolean = false,
val partial: Boolean,
val callback: Boolean
) : Definition
data class ExtensionInterfaceDefinition(val namespace: String, val name: String, val implements: String) : Definition
data class EnumDefinition(val namespace: String, val name: String, val entries: List<String>) : Definition
class ExtendedAttributeArgumentsParser(private val namespace: String) : WebIDLBaseVisitor<List<Attribute>>() {
private val arguments = ArrayList<Attribute>()
override fun defaultResult(): List<Attribute> = arguments
override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): List<Attribute> {
val attributeVisitor = AttributeVisitor(namespace = namespace)
attributeVisitor.visit(ctx)
val parameter = attributeVisitor.visitChildren(ctx)
arguments.add(parameter)
visitChildren(ctx)
return defaultResult()
}
}
// [Constructor]
// [Constructor(any, Int)]
// [Constructor(Int arg, String arg2 = "a")]
// [name = Constructor]
class ExtendedAttributeParser(private val namespace: String) : WebIDLBaseVisitor<ExtendedAttribute>() {
private var name: String? = null
private var call: String = ""
private val arguments = ArrayList<Attribute>()
override fun defaultResult(): ExtendedAttribute = ExtendedAttribute(name, call, arguments)
override fun visitExtendedAttribute(ctx: WebIDLParser.ExtendedAttributeContext): ExtendedAttribute {
call = ctx.children.filterIdentifiers().firstOrNull()?.text ?: ""
visitChildren(ctx)
return defaultResult()
}
override fun visitArgumentList(ctx: WebIDLParser.ArgumentListContext): ExtendedAttribute {
arguments.addAll(ExtendedAttributeArgumentsParser(namespace).visitChildren(ctx))
return defaultResult()
}
override fun visitIdentifierList(ctx: IdentifierListContext): ExtendedAttribute {
object : WebIDLBaseVisitor<Unit>() {
override fun visitTerminal(node: TerminalNode) {
if (node.symbol.type == WebIDLLexer.IDENTIFIER_WEBIDL) {
arguments.add(Attribute(node.text, AnyType(), true, vararg = false, static = false, required = false))
}
}
}.visitChildren(ctx)
return defaultResult()
}
override fun visitExtendedAttributeNamePart(ctx: WebIDLParser.ExtendedAttributeNamePartContext): ExtendedAttribute {
name = getName(ctx)
return defaultResult()
}
}
class UnionTypeVisitor(val namespace: String) : WebIDLBaseVisitor<List<Type>>() {
val list = ArrayList<Type>()
override fun defaultResult() = list
override fun visitUnionMemberType(ctx: WebIDLParser.UnionMemberTypeContext): List<Type> {
list.add(TypeVisitor(namespace).visitChildren(ctx))
return list
}
}
class TypeVisitor(val namespace: String) : WebIDLBaseVisitor<Type>() {
private var type: Type = AnyType()
private var awaitingSimpleType = false
override fun defaultResult() = type
override fun visitType(ctx: TypeContext?): Type {
type = super.visitType(ctx)
return type
}
override fun visitReturnType(ctx: ReturnTypeContext?): Type {
awaitingSimpleType = true
type = super.visitReturnType(ctx)
return type
}
override fun visitNonAnyType(ctx: WebIDLParser.NonAnyTypeContext): Type {
awaitingSimpleType = true
type = super.visitNonAnyType(ctx)
return type
}
override fun visitUnionType(ctx: WebIDLParser.UnionTypeContext): Type {
type = UnionType(namespace, UnionTypeVisitor(namespace).visitChildren(ctx), false)
return type
}
override fun visitPromiseType(ctx: PromiseTypeContext): Type {
type = PromiseType(TypeVisitor(namespace).visitChildren(ctx), false)
return type
}
override fun visitSequenceType(ctx: SequenceTypeContext): Type {
val mutable = ctx.getChild(0).text == "sequence"
type = ArrayType(TypeVisitor(namespace).visitChildren(ctx), mutable = mutable, nullable = false)
return type
}
override fun visitTypeSuffix(ctx: TypeSuffixContext): Type {
when (ctx.text?.trim()) {
"?" -> type = type.toNullable()
"[]" -> type = ArrayType(type, mutable = true, nullable = false)
"[]?" -> type = ArrayType(type, mutable = true, nullable = false)
"?[]" -> type = ArrayType(type.toNullable(), mutable = true, nullable = false)
}
return type
}
override fun visitNull_(ctx: Null_Context): Type {
if (ctx.text?.trim() == "?") {
type = type.toNullable()
}
return type
}
override fun visitTerminal(node: TerminalNode): Type {
if (awaitingSimpleType) {
type = SimpleType(node.text, false)
awaitingSimpleType = false
}
return type
}
override fun visitUnsignedIntegerType(ctx: UnsignedIntegerTypeContext): Type {
awaitingSimpleType = false
type = super.visitUnsignedIntegerType(ctx)
return type
}
override fun visitUnrestrictedFloatType(ctx: UnrestrictedFloatTypeContext): Type {
awaitingSimpleType = false
type = super.visitUnrestrictedFloatType(ctx)
return type
}
override fun visitFloatType(ctx: FloatTypeContext): Type {
type = SimpleType(ctx.text, false)
return type
}
override fun visitIntegerType(ctx: IntegerTypeContext): Type {
type = SimpleType(ctx.text, false)
return type
}
}
class OperationVisitor(private val attributes: List<ExtendedAttribute>, private val static: Boolean, private val namespace: String) : WebIDLBaseVisitor<Operation>() {
private var name: String = ""
private var returnType: Type = UnitType
private val parameters = ArrayList<Attribute>()
private val exts = ArrayList<ExtendedAttribute>()
override fun defaultResult() = Operation(name, returnType, parameters, attributes + exts, static)
override fun visitOptionalIdentifier(ctx: OptionalIdentifierContext): Operation {
name = ctx.text
return defaultResult()
}
override fun visitSpecial(ctx: WebIDLParser.SpecialContext): Operation {
if (ctx.children != null) {
exts.add(ExtendedAttribute(call = ctx.text, name = null, arguments = emptyList()))
}
return defaultResult()
}
override fun visitReturnType(ctx: ReturnTypeContext): Operation {
returnType = TypeVisitor(namespace).visit(ctx)
return defaultResult()
}
override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): Operation {
val attributeVisitor = AttributeVisitor(static = false, namespace = namespace)
attributeVisitor.visit(ctx)
val parameter = attributeVisitor.visitChildren(ctx)
parameters.add(parameter)
return defaultResult()
}
}
class AttributeVisitor(private val readOnly: Boolean = false, private val static: Boolean = false, private val namespace: String) : WebIDLBaseVisitor<Attribute>() {
private var type: Type = AnyType(true)
private var name: String = ""
private var defaultValue: String? = null
private var vararg: Boolean = false
private var required: Boolean = false
override fun defaultResult(): Attribute = Attribute(name, type, readOnly, defaultValue, vararg, static, required)
override fun visitType(ctx: WebIDLParser.TypeContext): Attribute {
type = TypeVisitor(namespace).visit(ctx)
return defaultResult()
}
override fun visitOptionalOrRequiredArgument(ctx: WebIDLParser.OptionalOrRequiredArgumentContext): Attribute {
if (ctx.children?.any { it is TerminalNode && it.text == "optional" } ?: false) {
defaultValue = "definedExternally"
}
if (ctx.children?.any { it is TerminalNode && it.text == "required" } ?: false) {
required = true
}
return visitChildren(ctx)
}
override fun visitAttributeRest(ctx: WebIDLParser.AttributeRestContext): Attribute {
name = getNameOrNull(ctx) ?: ctx.children.filter { it is TerminalNode }.filter { it.text != ";" }.last().text
return defaultResult()
}
override fun visitArgumentName(ctx: WebIDLParser.ArgumentNameContext): Attribute {
name = getNameOrNull(ctx) ?: ctx.text
return defaultResult()
}
override fun visitDefaultValue(ctx: WebIDLParser.DefaultValueContext): Attribute {
defaultValue = ctx.text
return defaultResult()
}
override fun visitEllipsis(ctx: WebIDLParser.EllipsisContext): Attribute {
vararg = vararg || "..." in ctx.text
return defaultResult()
}
}
class ConstantVisitor : WebIDLBaseVisitor<Constant>() {
private var type: Type = AnyType(false)
private var name: String = ""
private var value: String? = null
override fun defaultResult(): Constant = Constant(name, type, value)
override fun visitConst_(ctx: WebIDLParser.Const_Context): Constant {
name = getName(ctx)
return visitChildren(ctx)
}
override fun visitConstType(ctx: WebIDLParser.ConstTypeContext): Constant {
type = SimpleType(ctx.text, false)
return defaultResult()
}
override fun visitConstValue(ctx: WebIDLParser.ConstValueContext): Constant {
value = ctx.text
return defaultResult()
}
}
class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, val namespace: String, val declarations: MutableList<Definition>) : WebIDLBaseVisitor<Definition>() {
private var kind = DefinitionKind.INTERFACE
private var name = ""
private val memberAttributes = ArrayList<ExtendedAttribute>()
private val operations = ArrayList<Operation>()
private val attributes = ArrayList<Attribute>()
private var readOnly: Boolean = false
private var static: Boolean = false
private val inherited = ArrayList<String>()
private var typedefType: Type? = null
private var implements: String? = null
private val constants = ArrayList<Constant>()
private var partial = false
private var callback = false
private val enumEntries = mutableListOf<String>()
private var enumEntryExpected = false
override fun defaultResult(): Definition = when (kind) {
DefinitionKind.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, false, partial, callback)
DefinitionKind.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, /* dictionary = */ true, partial, callback)
DefinitionKind.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "")
DefinitionKind.TYPEDEF -> TypedefDefinition(typedefType ?: AnyType(true), namespace, name)
DefinitionKind.ENUM -> EnumDefinition(namespace, name, enumEntries)
}
override fun visitCallbackRestOrInterface(ctx: WebIDLParser.CallbackRestOrInterfaceContext): Definition {
callback = true
return visitChildren(ctx)
}
override fun visitCallbackRest(ctx: WebIDLParser.CallbackRestContext): Definition {
kind = DefinitionKind.TYPEDEF
name = getName(ctx)
val function = OperationVisitor(memberAttributes.toList(), static, namespace).visit(ctx)
typedefType = FunctionType(function.parameters, function.returnType, false)
memberAttributes.clear()
return defaultResult()
}
override fun visitModule(ctx: ModuleContext): Definition {
val moduleName = getName(ctx)
val namespace = if (this.namespace.endsWith(moduleName)) this.namespace else this.namespace + "." + moduleName
ModuleVisitor(declarations, namespace).visitChildren(ctx)
return defaultResult()
}
override fun visitInterface_(ctx: Interface_Context): Definition {
name = getName(ctx)
visitChildren(ctx)
return defaultResult()
}
override fun visitPartialInterface(ctx: WebIDLParser.PartialInterfaceContext): Definition {
name = getName(ctx)
partial = true
visitChildren(ctx)
return defaultResult()
}
override fun visitTypedef(ctx: WebIDLParser.TypedefContext): Definition {
if (name != "") {
// TODO temporary workaround for local typedefs
return defaultResult()
}
kind = DefinitionKind.TYPEDEF
name = getName(ctx)
typedefType = ctx.accept(object : WebIDLBaseVisitor<Type>() {
private var foundType: Type = AnyType(false)
override fun defaultResult(): Type = foundType
override fun visitType(ctx: WebIDLParser.TypeContext): Type {
foundType = TypeVisitor(namespace).visit(ctx)
return defaultResult()
}
})
return defaultResult()
}
override fun visitEnum_(ctx: Enum_Context): Definition {
enumEntryExpected = true
kind = DefinitionKind.ENUM
name = getName(ctx)
super.visitEnum_(ctx)
enumEntryExpected = false
return defaultResult()
}
override fun visitTerminal(node: TerminalNode): Definition {
if (enumEntryExpected && node.symbol.type == WebIDLParser.STRING_WEBIDL) {
enumEntries += node.symbol.text.removeSurrounding("\"", "\"")
}
return super.visitTerminal(node)
}
override fun visitDictionary(ctx: DictionaryContext): Definition {
kind = DefinitionKind.DICTIONARY
name = getName(ctx)
return visitChildren(ctx)
}
override fun visitDictionaryMember(ctx: DictionaryMemberContext): Definition {
val name = ctx.children
.filterIdentifiers()
.firstOrNull { it.text != "" }
?.text
val type = TypeVisitor(namespace).visit(ctx.children.first { it is TypeContext })
var required = false
val defaultValue = object : WebIDLBaseVisitor<String?>() {
private var value: String? = null
override fun defaultResult() = value
override fun visitDefaultValue(ctx2: DefaultValueContext): String? {
value = ctx2.text
return value
}
override fun visitRequired(ctx: RequiredContext?): String? {
if (ctx?.children?.any { it is TerminalNode && it.text == "required" } ?: false) {
required = true
}
return super.visitRequired(ctx)
}
}.visit(ctx)
attributes.add(Attribute(name ?: "", type, false, defaultValue, false, static, required))
return defaultResult()
}
override fun visitImplementsStatement(ctx: ImplementsStatementContext): Definition {
val identifiers = ctx.children.filterIdentifiers().map { it.text }
if (identifiers.size == 2) {
kind = DefinitionKind.EXTENSION_INTERFACE
name = identifiers[0]
implements = identifiers[1]
visitChildren(ctx)
}
return defaultResult()
}
override fun visitOperation(ctx: OperationContext): Definition {
visitOperationImpl(ctx)
return defaultResult()
}
private fun visitOperationImpl(ctx: ParserRuleContext) {
operations.add(OperationVisitor(memberAttributes.toList(), static, namespace).visit(ctx))
memberAttributes.clear()
}
override fun visitInheritance(ctx: WebIDLParser.InheritanceContext): Definition {
if (ctx.children != null) {
inherited.addAll(ctx.children.filterIdentifiers().map { it.text.trim() }.filter { it != "" })
}
return defaultResult()
}
override fun visitReadOnly(ctx: WebIDLParser.ReadOnlyContext): Definition {
return visitReadOnlyImpl(ctx)
}
override fun visitReadonlyMemberRest(ctx: ReadonlyMemberRestContext): Definition? {
return visitReadOnlyImpl(ctx)
}
private fun visitReadOnlyImpl(ctx: ParserRuleContext): Definition {
readOnly = true
visitChildren(ctx)
readOnly = false
return defaultResult()
}
override fun visitStaticMember(ctx: WebIDLParser.StaticMemberContext): Definition {
static = true
visitChildren(ctx)
static = false
return defaultResult()
}
override fun visitStaticMemberRest(ctx: WebIDLParser.StaticMemberRestContext): Definition {
if (ctx.children?.any { it is OperationRestContext } ?: false) {
visitOperationImpl(ctx)
} else {
visitChildren(ctx)
}
return defaultResult()
}
override fun visitAttributeRest(ctx: WebIDLParser.AttributeRestContext): Definition {
with(AttributeVisitor(readOnly, static, namespace)) {
visit(ctx)
this@DefinitionVisitor.attributes.add(visitChildren(ctx))
}
return defaultResult()
}
override fun visitConst_(ctx: WebIDLParser.Const_Context): Definition {
constants.add(ConstantVisitor().visit(ctx))
memberAttributes.clear()
return defaultResult()
}
override fun visitExtendedAttribute(ctx: ExtendedAttributeContext): Definition {
memberAttributes.add(ExtendedAttributeParser(namespace).visit(ctx))
return defaultResult()
}
}
class ModuleVisitor(val declarations: MutableList<Definition>, var namespace: String = "") : WebIDLBaseVisitor<Unit>() {
val extendedAttributes = ArrayList<ExtendedAttribute>()
override fun visitDefinition(ctx: WebIDLParser.DefinitionContext) {
val declaration = DefinitionVisitor(extendedAttributes.toList(), namespace, declarations).visitChildren(ctx)
extendedAttributes.clear()
declarations.add(declaration)
}
override fun visitExtendedAttribute(ctx: ExtendedAttributeContext?) {
val att = with(ExtendedAttributeParser(namespace)) {
visit(ctx)
}
extendedAttributes.add(att)
}
override fun visitNamespaceRest(ctx: NamespaceRestContext) {
this.namespace = ctx.text
}
}
private fun List<ParseTree>?.filterIdentifiers(): List<ParseTree> = this?.filter { it is TerminalNode && it.symbol.type == WebIDLLexer.IDENTIFIER_WEBIDL } ?: emptyList()
private fun getName(ctx: ParserRuleContext) = ctx.children.filterIdentifiers().first().text
private fun getNameOrNull(ctx: ParserRuleContext) = ctx.children.filterIdentifiers().firstOrNull()?.text
fun parseIDL(reader: CharStream): Repository {
val ll = WebIDLLexer(reader)
val pp = WebIDLParser(CommonTokenStream(ll))
val idl = pp.webIDL()
val declarations = ArrayList<Definition>()
ModuleVisitor(declarations).visit(idl)
return Repository(
declarations.filterIsInstance<InterfaceDefinition>().filter { it.name.isEmpty().not() }.groupBy { it.name }.mapValues { it.value.reduce(::merge) },
declarations.filterIsInstance<TypedefDefinition>().groupBy { it.name }.mapValues { it.value.first() },
declarations.filterIsInstance<ExtensionInterfaceDefinition>().groupBy { it.name }.mapValues { it.value.map { it.implements } },
declarations.filterIsInstance<EnumDefinition>().groupBy { it.name }.mapValues { it.value.reduce { a, _ -> a } }
)
}
fun merge(i1: InterfaceDefinition, i2: InterfaceDefinition): InterfaceDefinition {
require(i1.name == i2.name)
return InterfaceDefinition(i1.name,
namespace = if (i1.partial) i2.namespace else i1.namespace,
extendedAttributes = i1.extendedAttributes merge i2.extendedAttributes,
operations = i1.operations merge i2.operations,
attributes = i1.attributes merge i2.attributes,
superTypes = i1.superTypes merge i2.superTypes,
constants = i1.constants merge i2.constants,
dictionary = i1.dictionary || i2.dictionary,
partial = i1.partial && i2.partial,
callback = i1.callback && i2.callback
)
}
infix fun <T> List<T>.merge(other: List<T>) = (this + other).distinct()
@@ -1,15 +0,0 @@
package org.jetbrains.idl2k
import org.jetbrains.idl2k.util.readCopyrightNoticeFromProfile
import java.io.*
fun main(args: Array<String>) {
val webIdl = BuildWebIdl(
mdnCacheFile = File("target/mdn-cache.txt"),
srcDir = File("../../stdlib/js/idl"))
println("Generating...")
val copyrightNotice = readCopyrightNoticeFromProfile(File("../../../.idea/copyright/apache.xml"))
webIdl.jsGenerator(File("../../stdlib/js/src/org.w3c"), copyrightNotice)
}
@@ -1,38 +0,0 @@
package org.jetbrains.idl2k
import java.io.*
class MDNDocumentationCache(val existing: Set<String>, val nonExisting: Set<String>) {
fun checkInCache(url: String): Boolean? = when (url) {
in existing -> true
in nonExisting -> false
else -> null
}
companion object {
val Empty = MDNDocumentationCache(emptySet(), emptySet())
fun read(file: File): MDNDocumentationCache {
val existing = HashSet<String>()
val nonExisting = HashSet<String>()
file.forEachLine { line ->
val parts = line.split("|")
if (parts.size == 2) {
val url = parts[0]
if (parts[1] == "Y") existing.add(url)
else if (parts[1] == "N") nonExisting.add(url)
}
}
return MDNDocumentationCache(existing, nonExisting)
}
fun writeTo(c: MDNDocumentationCache, file: File) {
file.bufferedWriter().use {
(c.existing + c.nonExisting).sorted().joinTo(it, separator = "\n") { "$it|${if (it in c.existing) "Y" else "N"}" }
}
}
}
}
@@ -1,104 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.idl2k
data class NamedValue<V>(val name: String, val value: V)
data class Repository(
val interfaces: Map<String, InterfaceDefinition>,
val typeDefs: Map<String, TypedefDefinition>,
val externals: Map<String, List<String>>,
val enums: Map<String, EnumDefinition>
)
enum class AttributeKind {
VAL, VAR, ARGUMENT
}
data class GenerateAttribute(val name: String, val type: Type, val initializer: String?, val getterSetterNoImpl: Boolean, val kind: AttributeKind, val override: Boolean, var vararg: Boolean, val static: Boolean, val required: Boolean)
val GenerateAttribute.getterNoImpl: Boolean
get() = getterSetterNoImpl
val GenerateAttribute.setterNoImpl: Boolean
get() = getterSetterNoImpl && kind == AttributeKind.VAR
val GenerateAttribute.isVal: Boolean
get() = kind == AttributeKind.VAL
val GenerateAttribute.isVar: Boolean
get() = kind == AttributeKind.VAR
val Type.typeSignature: String
get() = when {
this is FunctionType -> "Function$arity"
else -> this.toString()
}
val GenerateAttribute.signature: String
get() = "$name:${type.typeSignature}"
fun GenerateAttribute.dynamicIfUnknownType(allTypes : Set<String>, standardTypes : Set<Type> = standardTypes()) = copy(type = type.dynamicIfUnknownType(allTypes, standardTypes))
fun List<GenerateAttribute>.dynamicIfUnknownType(allTypes : Set<String>, standardTypes : Set<Type> = standardTypes()) = map { it.dynamicIfUnknownType(allTypes, standardTypes) }
enum class NativeGetterOrSetter {
NONE,
GETTER,
SETTER
}
enum class GenerateDefinitionKind {
INTERFACE,
CLASS,
ABSTRACT_CLASS
}
data class GenerateFunction(
val name: String,
val returnType: Type,
val arguments: List<GenerateAttribute>,
val nativeGetterOrSetter: NativeGetterOrSetter,
val static: Boolean,
val override: Boolean
)
data class ConstructorWithSuperTypeCall(val constructor: GenerateFunction, val constructorAttribute: ExtendedAttribute)
data class GenerateClass(
val name: String,
val namespace: String,
val kind: GenerateDefinitionKind,
val superTypes: List<String>,
val memberAttributes: MutableList<GenerateAttribute>,
val memberFunctions: MutableList<GenerateFunction>,
val constants: List<GenerateAttribute>,
val primaryConstructor: ConstructorWithSuperTypeCall?,
val secondaryConstructors: List<ConstructorWithSuperTypeCall>,
val generateBuilderFunction: Boolean
)
val GenerateFunction.signature: String
get() = arguments.map { it.type.typeSignature }.joinToString(", ", "$name(", ")")
fun GenerateFunction.dynamicIfUnknownType(allTypes : Set<String>) = standardTypes().let { standardTypes ->
copy(returnType = returnType.dynamicIfUnknownType(allTypes, standardTypes), arguments = arguments.map { it.dynamicIfUnknownType(allTypes, standardTypes) })
}
fun InterfaceDefinition.findExtendedAttributes(name: String) = extendedAttributes.filter { it.name == name }
fun InterfaceDefinition.findConstructors() = extendedAttributes.filter { it.call == "Constructor" }
data class GenerateUnionTypes(
val typeNamesToUnionsMap: Map<String, List<String>>,
val anonymousUnionsMap: Map<String, GenerateClass>,
val typedefsMarkersMap: Map<String, GenerateClass>
)
@@ -1,428 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* 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 org.jetbrains.idl2k
import org.jetbrains.idl2k.util.mapEnumConstant
import java.math.BigInteger
private fun <O : Appendable> O.indent(commented: Boolean = false, level: Int) {
if (commented) {
append("//")
}
for (i in 1..level) {
append(" ")
}
}
private fun Appendable.renderAttributeDeclaration(arg: GenerateAttribute, modality: MemberModality, omitDefaults: Boolean = false) {
if (arg.vararg) {
append("vararg ")
}
else {
when (modality) {
MemberModality.OVERRIDE -> append("override ")
MemberModality.ABSTRACT -> append("abstract ")
MemberModality.OPEN -> append("open ")
MemberModality.FINAL -> {}
}
}
append(when(arg.kind) {
AttributeKind.VAL -> "val "
AttributeKind.VAR -> "var "
AttributeKind.ARGUMENT -> ""
})
append(arg.name.replaceKeywords())
append(": ")
append(arg.type.render())
if (arg.initializer != null) {
if (omitDefaults) {
append(" /*")
}
append(" = ")
append(arg.initializer.specifyInitializerConstant(arg.type))
if (omitDefaults) {
append(" */")
}
}
}
private fun Appendable.renderAttributeDeclarationAsProperty(arg: GenerateAttribute, modality: MemberModality, commented: Boolean, level: Int, omitDefaults: Boolean = false) {
indent(commented, level)
if (arg.name in keywords) {
append("@JsName(\"${arg.name}\") ")
}
renderAttributeDeclaration(arg, modality, omitDefaults)
appendln()
if (arg.getterNoImpl) {
indent(commented, level + 1)
appendln("get() = definedExternally")
}
if (arg.setterNoImpl) {
indent(commented, level + 1)
appendln("set(value) = definedExternally")
}
}
private val keywords = setOf("interface", "is", "as")
private fun String.parse() = if (this.startsWith("0x")) BigInteger(this.substring(2), 16) else BigInteger(this)
private fun String.specifyInitializerConstant(type: Type) = when {
this == "undefined" && type.nullable -> "undefined"
this == "definedExternally" || type is SimpleType && type.type == "Int" && parse() > BigInteger.valueOf(Int.MAX_VALUE.toLong()) -> "definedExternally"
type is SimpleType && type.type == "Double" && this.matches("[0-9]+".toRegex()) -> "${this}.0"
type is SimpleType && type.type == "Float" -> "${this}f"
else -> this
}
private fun String.replaceKeywords() = if (this in keywords) this + "_" else this
private fun Appendable.renderArgumentsDeclaration(args: List<GenerateAttribute>, omitDefaults: Boolean = false) =
args.joinTo(this, ", ", "(", ")") {
StringBuilder().apply { renderAttributeDeclaration(it, if (it.override) MemberModality.OVERRIDE else MemberModality.FINAL, omitDefaults) }
}
private fun Appendable.renderFunctionDeclaration(owner: String, f: GenerateFunction, override: Boolean, commented: Boolean, level: Int = 1) {
indent(commented, level)
if (f.nativeGetterOrSetter != NativeGetterOrSetter.NONE) {
append("@kotlin.internal.InlineOnly ")
}
if (override) {
append("override ")
}
if (f.nativeGetterOrSetter != NativeGetterOrSetter.NONE) {
append("inline operator ")
}
if (f.name in keywords) {
append("@JsName(\"${f.name}\") ")
}
append("fun ")
if (f.nativeGetterOrSetter != NativeGetterOrSetter.NONE) {
append("$owner.")
}
append(f.name.replaceKeywords())
renderArgumentsDeclaration(f.arguments, override)
append(": ${f.returnType.render()}")
when (f.nativeGetterOrSetter) {
NativeGetterOrSetter.GETTER -> {
append(" = asDynamic()[${f.arguments[0].name}]")
}
NativeGetterOrSetter.SETTER -> {
append(" { asDynamic()[${f.arguments[0].name}] = ${f.arguments[1].name}; }")
}
NativeGetterOrSetter.NONE -> {}
}
appendln()
}
private fun List<GenerateAttribute>.hasNoVars() = none { it.isVar }
private fun GenerateAttribute.isCommented(parent: String) = "$parent.$name" in commentOutDeclarations || "$parent.$name: ${type.render()}" in commentOutDeclarations
private fun GenerateFunction.isCommented(parent: String) =
"$parent.$name" in commentOutDeclarations || "$parent.$name(${arguments.size})" in commentOutDeclarations
private fun GenerateAttribute.isRequiredFunctionArgument(owner: String, functionName: String) = "$owner.$functionName.$name" in requiredArguments
private fun GenerateFunction.fixRequiredArguments(parent: String) = copy(arguments = arguments.map { arg -> arg.copy(initializer = if (arg.isRequiredFunctionArgument(parent, name)) null else arg.initializer) })
fun Appendable.render(allTypes: Map<String, GenerateClass>, enums: List<EnumDefinition>, typeNamesToUnions: Map<String, List<String>>, iface: GenerateClass, markerAnnotation: Boolean = false, mdnCache: MDNDocumentationCache? = null) {
val url = "https://developer.mozilla.org/en/docs/Web/API/${iface.name}"
if (mdnCache?.checkInCache(url) == true) {
appendln("/**")
appendln(" * Exposes the JavaScript [${iface.name}]($url) to Kotlin")
appendln(" */")
}
val allTypesAndEnums = allTypes.keys + enums.map { it.name }
append("public external ")
if (markerAnnotation) {
append("@marker ")
}
when (iface.kind) {
GenerateDefinitionKind.CLASS -> append("open class ")
GenerateDefinitionKind.ABSTRACT_CLASS -> append("abstract class ")
GenerateDefinitionKind.INTERFACE -> append("interface ")
}
val allSuperTypes = iface.allSuperTypes(allTypes + kotlinBuiltinInterfaces)
val allSuperTypesNames = allSuperTypes.map { it.name }.toSet()
append(iface.name)
val primary = iface.primaryConstructor
if (primary != null && (primary.constructor.arguments.isNotEmpty() || iface.secondaryConstructors.isNotEmpty())) {
renderArgumentsDeclaration(primary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false)
}
val superTypesExclude = inheritanceExclude[iface.name] ?: emptySet()
val superTypesWithCalls =
iface.superTypes.filter { it in allSuperTypesNames }.filter { it !in superTypesExclude } +
(typeNamesToUnions[iface.name] ?: emptyList()) +
(iface.superTypes.filter { it.substringBefore("<") in kotlinBuiltinInterfaces }) // TODO in theory we have to parse type but for now it is the only place needs it so let's just cut string
if (superTypesWithCalls.isNotEmpty()) {
superTypesWithCalls.joinTo(this, ", ", " : ")
}
appendln (" {")
iface.secondaryConstructors.forEach { secondary ->
indent(false, 1)
append("constructor")
renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false)
appendln()
}
val superAttributes = allSuperTypes.flatMap { it.memberAttributes }.distinct()
val superAttributesByName = superAttributes.groupBy { it.name }
val superFunctions = allSuperTypes.flatMap { it.memberFunctions }.distinct()
val superSignatures = superAttributes.map { it.signature } merge superFunctions.map { it.signature }
iface.memberAttributes
.filter {
!it.static
&& (it.isVar || (it.isVal && superAttributesByName[it.name]?.hasNoVars() ?: true))
}
.map { it.dynamicIfUnknownType(allTypesAndEnums) }
.groupBy { it.name }
.mapValues { it.value.filter { "${iface.name}.${it.name}" !in commentOutDeclarations && "${iface.name}.${it.name}: ${it.type.render()}" !in commentOutDeclarations } }
.filterValues { it.isNotEmpty() }
.reduceValues(::merge).values.forEach { attribute ->
val modality = when {
attribute.signature in superSignatures -> MemberModality.OVERRIDE
iface.kind == GenerateDefinitionKind.CLASS && attribute.isVal -> MemberModality.OPEN
iface.kind == GenerateDefinitionKind.ABSTRACT_CLASS -> MemberModality.OPEN
else -> MemberModality.FINAL
}
val skipAttributeDeclaration = modality == MemberModality.OVERRIDE
&& attribute.kindNotChanged(superAttributesByName)
&& (iface.kind == GenerateDefinitionKind.INTERFACE || attribute.hasSuperImplementation(allSuperTypes))
if (attribute.name in superAttributesByName && attribute.signature !in superSignatures) {
System.err.println("Property ${iface.name}.${attribute.name} has different type in super type(s) so will not be generated: ")
for ((superTypeName, attributes) in allSuperTypes.map { it.name to it.memberAttributes.filter { it.name == attribute.name }.distinct() }) {
for (superAttribute in attributes) {
System.err.println(" $superTypeName.${attribute.name}: ${superAttribute.type.render()}")
}
}
} else if (skipAttributeDeclaration) {
// then don't generate
} else {
renderAttributeDeclarationAsProperty(attribute,
modality = modality,
commented = attribute.isCommented(iface.name),
omitDefaults = iface.kind == GenerateDefinitionKind.INTERFACE,
level = 1
)
}
}
val memberFunctions= iface.memberFunctions.filter {
(it !in superFunctions || it.override) && !it.static
}.map { it.dynamicIfUnknownType(allTypesAndEnums) }.groupBy { it.signature }.reduceValues(::betterFunction).values
fun doRenderFunction(function: GenerateFunction, level: Int = 1) {
renderFunctionDeclaration(
iface.name, function.fixRequiredArguments(iface.name),
function.signature in superSignatures || function.override,
commented = function.isCommented(iface.name),
level = level
)
}
memberFunctions.filter { it.nativeGetterOrSetter == NativeGetterOrSetter.NONE }.forEach { doRenderFunction(it) }
val staticAttributes = iface.memberAttributes.filter { it.static }
val staticFunctions = iface.memberFunctions.filter { it.static }
if (iface.constants.isNotEmpty() || staticAttributes.isNotEmpty() || staticFunctions.isNotEmpty()) {
appendln()
indent(false, 1)
appendln("companion object {")
iface.constants.forEach {
renderAttributeDeclarationAsProperty(it, MemberModality.FINAL, level = 2, commented = it.isCommented(iface.name))
}
staticAttributes.forEach {
renderAttributeDeclarationAsProperty(it, MemberModality.FINAL, level = 2, commented = it.isCommented(iface.name))
}
staticFunctions.forEach {
renderFunctionDeclaration(iface.name, it.fixRequiredArguments(iface.name), override = false, level = 2, commented = it.isCommented(iface.name))
}
indent(false, 1)
appendln("}")
}
appendln("}")
memberFunctions.filter { it.nativeGetterOrSetter != NativeGetterOrSetter.NONE }.forEach { doRenderFunction(it, 0) }
appendln()
if (iface.generateBuilderFunction) {
renderBuilderFunction(iface, allSuperTypes, allTypesAndEnums)
}
}
private fun GenerateAttribute.kindNotChanged(superAttributesByName: Map<String, List<GenerateAttribute>>) = superAttributesByName[name].orEmpty().all { it.kind == kind }
private fun GenerateAttribute.hasSuperImplementation(allSuperTypes: List<GenerateClass>) = allSuperTypes.any { st -> st.kind != GenerateDefinitionKind.INTERFACE && st.memberAttributes.any { it.signature == signature } }
private fun GenerateAttribute.hasNoDefaultValue() =
this.initializer == null && (this.type.nullable || this.type == DynamicType) && !this.required
fun Appendable.renderBuilderFunction(dictionary: GenerateClass, allSuperTypes: List<GenerateClass>, allTypes: Set<String>) {
val fields = (dictionary.memberAttributes + allSuperTypes.flatMap { it.memberAttributes })
.distinctBy { it.signature }
.map { it.copy(kind = AttributeKind.ARGUMENT) }
.dynamicIfUnknownType(allTypes)
.map { if (it.hasNoDefaultValue()) it.copy(initializer = "undefined") else it }
appendln("@kotlin.internal.InlineOnly")
append("public inline fun ${dictionary.name}")
renderArgumentsDeclaration(fields)
appendln(": ${dictionary.name} {")
indent(level = 1)
appendln("val o = js(\"({})\")")
appendln()
for (field in fields) {
indent(level = 1)
val escapedFieldName = field.name.replaceKeywords()
val nullGuardedAssignment = field.hasNoDefaultValue()
if (nullGuardedAssignment) {
appendln("if ($escapedFieldName !== undefined) {")
indent(level = 2)
}
appendln("o[\"${field.name}\"] = $escapedFieldName")
if (nullGuardedAssignment) {
indent(level = 1)
appendln("}")
}
}
appendln()
indent(level = 1)
appendln("return o")
appendln("}")
appendln()
}
fun betterFunction(f1: GenerateFunction, f2: GenerateFunction): GenerateFunction =
f1.copy(
arguments = f1.arguments
.zip(f2.arguments)
.map { it.first.copy(type = it.map { it.type }.betterType(), name = it.map { it.name }.betterName()) },
nativeGetterOrSetter = listOf(f1.nativeGetterOrSetter, f2.nativeGetterOrSetter)
.firstOrNull { it != NativeGetterOrSetter.NONE } ?: NativeGetterOrSetter.NONE
)
private fun <F, T> Pair<F, F>.map(block: (F) -> T) = block(first) to block(second)
private fun Pair<Type, Type>.betterType() = if (first is DynamicType || first is AnyType) first else second
private fun Pair<String, String>.betterName() = if (((0..9).map(Int::toString) + listOf("arg")).none { first.toLowerCase().contains(it) }) first else second
private fun merge(a: AttributeKind, b: AttributeKind): AttributeKind {
if (a == b) {
return a
}
if (a == AttributeKind.VAR || b == AttributeKind.VAR) {
return AttributeKind.VAR
}
return a
}
private fun merge(a: GenerateAttribute, b: GenerateAttribute): GenerateAttribute {
require(a.name == b.name)
val type = when {
a.type.dropNullable() == b.type.dropNullable() -> a.type.withNullability(a.type.nullable || b.type.nullable)
else -> DynamicType
}
return GenerateAttribute(
a.name,
type,
a.initializer ?: b.initializer,
a.getterSetterNoImpl || b.getterSetterNoImpl,
merge(a.kind, b.kind),
a.override,
a.vararg,
a.static,
a.required || b.required
)
}
fun <K, V> List<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = groupBy { it.first }.mapValues { it.value.map { it.second } }
fun Appendable.render(enumDefinition: EnumDefinition) {
appendln("/* please, don't implement this interface! */")
appendln("public external interface ${enumDefinition.name} {")
indent(level = 1)
appendln("companion object")
appendln("}")
for (entry in enumDefinition.entries) {
val entryName = mapEnumConstant(entry)
appendln("public inline val ${enumDefinition.name}.Companion.$entryName: ${enumDefinition.name} " +
"get() = \"$entry\".asDynamic().unsafeCast<${enumDefinition.name}>()")
}
appendln()
}
fun Appendable.render(namespace: String, ifaces: List<GenerateClass>, unions: GenerateUnionTypes, enums: List<EnumDefinition>, mdnCache: MDNDocumentationCache) {
val declaredTypes = ifaces.associateBy { it.name }
val allTypes = declaredTypes + unions.anonymousUnionsMap + unions.typedefsMarkersMap
declaredTypes.values.filter { it.namespace == namespace }.forEach {
render(allTypes, enums, unions.typeNamesToUnionsMap, it, mdnCache = mdnCache)
}
unions.anonymousUnionsMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach {
render(allTypes, enums, emptyMap(), it, markerAnnotation = true)
}
unions.typedefsMarkersMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach {
render(allTypes, enums, emptyMap(), it, markerAnnotation = true)
}
enums.filter { it.namespace == namespace }
.forEach { render(it) }
}
enum class MemberModality {
OPEN,
ABSTRACT,
OVERRIDE,
FINAL
}
@@ -1,150 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.idl2k
import java.util.*
private val typeMapper = mapOf(
"unsignedlong" to SimpleType("Int", false),
"unsignedlonglong" to SimpleType("Int", false),
"longlong" to SimpleType("Int", false),
"unsignedshort" to SimpleType("Short", false),
"unsignedbyte" to SimpleType("Byte", false),
"octet" to SimpleType("Byte", false),
"void" to UnitType,
"boolean" to SimpleType("Boolean", false),
"byte" to SimpleType("Byte", false),
"short" to SimpleType("Short", false),
"long" to SimpleType("Int", false),
"float" to SimpleType("Float", false),
"double" to SimpleType("Double", false),
"any" to AnyType(true),
"DOMTimeStamp" to SimpleType("Number", false),
"object" to DynamicType, // TODO map to Any?
"WindowProxy" to SimpleType("Window", false),
"USVString" to SimpleType("String", false),
"DOMString" to SimpleType("String", false),
"ByteString" to SimpleType("String", false),
"DOMError" to DynamicType,
"Elements" to DynamicType,
"Date" to SimpleType("Date", false),
"" to DynamicType
)
fun GenerateClass.allSuperTypes(all: Map<String, GenerateClass>) = LinkedHashSet<GenerateClass>().let { result -> allSuperTypesImpl(listOf(this), all, result); result.toList() }
tailrec fun allSuperTypesImpl(roots: List<GenerateClass>, all: Map<String, GenerateClass>, result: MutableSet<GenerateClass>) {
if (roots.isNotEmpty()) {
allSuperTypesImpl(roots.flatMap { it.superTypes }.map {
all[it] ?: all[it.substringBefore("<")]
}.filterNotNull().filter { result.add(it) }, all, result)
}
}
fun standardTypes() = typeMapper.values.map { it.dropNullable() }.toSet()
fun Type.dynamicIfUnknownType(allTypes: Set<String>, standardTypes: Set<Type> = standardTypes()): Type = when {
this is DynamicType || this is UnitType -> this
this is SimpleType && this.type in allTypes -> this
this.dropNullable() in standardTypes -> this
this is ArrayType -> copy(memberType = this.memberType.dynamicIfUnknownType(allTypes, standardTypes))
this is UnionType -> if (this.name !in allTypes) DynamicType else this
this is FunctionType -> copy(
returnType = returnType.dynamicIfUnknownType(allTypes, standardTypes),
parameterTypes = parameterTypes.map {
it.copy(
type = it.type.dynamicIfUnknownType(
allTypes,
standardTypes
)
)
})
this is PromiseType ->
copy(valueType = valueType.dynamicIfUnknownType(allTypes, standardTypes))
else -> DynamicType
}
private fun Type.dynamicIfAnyType(): Type = if (this is AnyType && this.nullable) DynamicType else this
internal fun mapType(repository: Repository, type: Type): Type = when (type) {
is SimpleType -> {
val typeName = type.type
when {
typeName in typeMapper -> typeMapper[typeName]!!.withNullability(type.nullable)
typeName in repository.interfaces -> type
typeName in repository.typeDefs -> mapTypedef(repository, type)
else -> type
}
}
is PromiseType -> type.copy(valueType = mapType(repository, type.valueType))
is ArrayType -> type.copy(memberType = mapType(repository, type.memberType))
is UnionType -> UnionType(
type.namespace,
type.memberTypes.map { mt -> mapType(repository, mt) },
type.nullable
).toSingleTypeIfPossible()
is FunctionType -> type.copy(
// TODO: Remove takeWhile { !vararg } when we have varargs supported. See KT-3115
returnType = mapType(repository, type.returnType).dynamicIfAnyType(),
parameterTypes = type.parameterTypes.takeWhile { !it.vararg }.map { it.copy(type = mapType(repository, it.type)) }
)
is AnyType,
is UnitType,
is DynamicType -> type
}
private fun mapTypedef(repository: Repository, type: SimpleType): Type {
val typedef = repository.typeDefs[type.type]!!
return when {
typedef.types is UnionType && typedef.types.memberTypes.size == 1 -> mapType(
repository,
typedef.types.memberTypes.single().withNullability(type.nullable)
)
typedef.types is UnionType -> SimpleType(typedef.name, type.nullable)
else -> mapType(repository, typedef.types.withNullability(type.nullable))
}
}
private fun GenerateFunction?.allTypes() =
if (this != null) sequenceOf(returnType) + arguments.asSequence().map { it.type } else emptySequence()
internal fun collectUnionTypes(allTypes: Map<String, GenerateClass>) =
allTypes.values.asSequence()
.flatMap {
it.secondaryConstructors.asSequence().flatMap { it.constructor.allTypes() } +
sequenceOf(it.primaryConstructor).filterNotNull().flatMap { it.constructor.allTypes() } +
it.memberAttributes.asSequence().map { it.type } +
it.memberFunctions.asSequence().flatMap { it.allTypes() }
}
.filterIsInstance<UnionType>()
.map { it.dropNullable() }
.filter { it.memberTypes.all { unionMember -> unionMember is SimpleType && unionMember.type in allTypes } }
.distinct()
.map { it.copy(namespace = guessPackage(it.memberTypes.filterIsInstance<SimpleType>().map { it.type }, allTypes), types = it.memberTypes) }
private fun guessPackage(types : List<String>, allTypes: Map<String, GenerateClass>) =
types.map { allTypes[it] }
.map { it?.namespace }
.filterNotNull()
.filter { it.isNotEmpty() }
.distinct()
.minBy { it.split('.').size } ?: ""
@@ -1,92 +0,0 @@
package org.jetbrains.idl2k
import java.util.*
sealed class Type {
abstract val nullable: Boolean
abstract fun render(): String
}
private fun String.appendNullabilitySuffix(type: Type) = if (type.nullable) "$this?" else this
object UnitType : Type() {
override val nullable: Boolean
get() = false
override fun render() = "Unit"
}
object DynamicType : Type() {
override val nullable: Boolean
get() = false
override fun render() = "dynamic"
}
data class AnyType(override val nullable: Boolean = true) : Type() {
override fun render() = "Any".appendNullabilitySuffix(this)
}
data class SimpleType(val type: String, override val nullable: Boolean) : Type() {
override fun render() = type.appendNullabilitySuffix(this)
}
data class FunctionType(val parameterTypes : List<Attribute>, val returnType : Type, override val nullable: Boolean) : Type() {
override fun render() = if (nullable) "(${renderImpl()})?" else renderImpl()
private fun renderImpl() = "(${parameterTypes.joinToString(", ") { it.type.render() }}) -> ${returnType.render()}"
}
data class PromiseType(val valueType: Type, override val nullable: Boolean) : Type() {
override fun render() = "Promise<${valueType.render()}>".appendNullabilitySuffix(this)
}
val FunctionType.arity : Int
get() = parameterTypes.size
class UnionType(val namespace: String, types: Collection<Type>, override val nullable: Boolean) : Type() {
val memberTypes: Set<Type> = LinkedHashSet(types.sortedBy { it.toString() })
val name = "Union${this.memberTypes.map { it.render() }.joinToString("Or")}"
operator fun contains(type: Type) = type in memberTypes
override fun equals(other: Any?): Boolean = other is UnionType && memberTypes == other.memberTypes
override fun hashCode(): Int = memberTypes.hashCode()
override fun toString(): String = memberTypes.joinToString(", ", "Union<", ">")
override fun render(): String = name.appendNullabilitySuffix(this)
fun copy(namespace: String = this.namespace, types: Collection<Type> = this.memberTypes, nullable: Boolean = this.nullable) =
UnionType(namespace, types, nullable)
}
fun UnionType.toSingleTypeIfPossible() = if (this.memberTypes.size == 1) this.memberTypes.single().withNullability(nullable) else this
data class ArrayType(val memberType: Type, val mutable: Boolean, override val nullable: Boolean) : Type() {
override fun render(): String = "Array<${if (mutable) "" else "out "}${memberType.render()}>".appendNullabilitySuffix(this)
}
@Suppress("UNCHECKED_CAST")
private fun <T: Type> T.copyWithNullability(nullable: Boolean): T = when (this) {
is UnitType -> UnitType
is DynamicType -> this
is AnyType -> this.copy(nullable = nullable)
is SimpleType -> this.copy(nullable = nullable)
is FunctionType -> this.copy(nullable = nullable)
is UnionType -> this.copy(types = this.memberTypes, nullable = nullable)
is ArrayType -> this.copy(nullable = nullable)
is PromiseType -> this.copy(nullable = nullable)
else -> throw UnsupportedOperationException()
} as T
private fun <T: Type> T.withNullabilityImpl(nullable: Boolean): T = if (this.nullable == nullable) this else copyWithNullability(nullable)
fun <T: Type> T.withNullability(nullable: Boolean): T = withNullabilityImpl(this.nullable or nullable)
fun <T: Type> T.toNullable(): T = withNullabilityImpl(true)
fun <T: Type> T.dropNullable(): T = withNullabilityImpl(false)
@Suppress("UNCHECKED_CAST")
fun <T: Type> T.toNullableIfNonPrimitive(): T = when (this) {
is UnitType -> UnitType
is DynamicType -> DynamicType
is SimpleType -> when (this.type) {
"Int", "Short", "Byte", "Float", "Double", "Boolean", "Long" -> this
else -> this.toNullable()
}
else -> this.toNullable()
} as T
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.idl2k.util
import org.xml.sax.InputSource
import java.io.File
import javax.xml.xpath.XPathFactory
fun readCopyrightNoticeFromProfile(copyrightProfile: File): String {
val template = copyrightProfile.reader().use { reader ->
XPathFactory.newInstance().newXPath().evaluate("/component/copyright/option[@name='notice']/@value", InputSource(reader))
}
val yearTemplate = "&#36;today.year"
val year = java.time.LocalDate.now().year.toString()
assert(yearTemplate in template)
return template.replace(yearTemplate, year).lines().joinToString("", prefix = "/*\n", postfix = " */\n") { " * $it\n" }
}
@@ -1,45 +0,0 @@
package org.jetbrains.idl2k.util
import java.util.*
fun List<List<*>>.mutationsCount() = if (isEmpty()) 0 else fold(1) { acc, e -> acc * e.size }
fun <T> List<List<T>>.mutations() : List<List<T>> {
val indices = IntArray(size)
val sizes = map { it.size }
fun next() : Boolean {
var carry = 1
for (pos in size - 1 downTo 0) {
var index = indices[pos]
val size = sizes[pos]
index += carry
carry = (index - size + 1).coerceAtLeast(0)
if (index >= size) {
indices[pos] = index - size
} else {
indices[pos] = index
return true
}
}
return carry == 0
}
val count = mutationsCount()
if (count == 0) {
return emptyList()
}
val result = ArrayList<List<T>>(count)
do {
result.add(indices.mapIndexed { pos, index -> this[pos][index] })
} while (next())
return result
}
fun mapEnumConstant(entry: String) = if (entry.isEmpty()) "EMPTY" else entry.toUpperCase().replace("-", "_")
@@ -1,15 +0,0 @@
#!/bin/bash
mkdir -p ~/tmp/ca
cd ~/tmp/ca
curl http://www.startssl.com/certs/ca.crt -O
curl http://www.startssl.com/certs/sub.class1.server.ca.crt -O
curl http://www.startssl.com/certs/sub.class2.server.ca.crt -O
curl http://www.startssl.com/certs/sub.class3.server.ca.crt -O
curl http://www.startssl.com/certs/sub.class4.server.ca.crt -O
for crt in *.crt; do
keytool -import -trustcacerts -keystore ${JAVA_HOME}/jre/lib/security/cacerts -storepass changeit -noprompt -alias ${crt} -file ${crt};
done
@@ -1,52 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.jetbrains.idl2k.BuildWebIdl
import org.jetbrains.idl2k.render
import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.File
import java.io.StringWriter
import java.io.Writer
class Idl2kTests {
private fun convertIdlToWriter(file: File): Writer {
val nonExistentCache = File.createTempFile("mdnCache", System.nanoTime().toString())
val buildWebIdl = BuildWebIdl(nonExistentCache, file)
val stringWriter = StringWriter()
stringWriter.render(
"",
buildWebIdl.definitions,
buildWebIdl.unions,
buildWebIdl.repository.enums.values.toList(),
buildWebIdl.mdnCache
)
nonExistentCache.delete()
return stringWriter
}
private fun assertIdlCompiledTo(fileName: String, expectedOutputName: String) {
val testResourcePrefix = "src/test/resources/"
assertEquals(
File(testResourcePrefix).resolve(expectedOutputName).readText(),
convertIdlToWriter(File(testResourcePrefix).resolve(fileName)).toString()
)
}
@Test
fun basicTest() {
assertIdlCompiledTo(
"SomethingNotInCache.idl",
"SomethingNotInCache.kt"
)
}
}
@@ -1,8 +0,0 @@
[Constructor]
interface SomethingNotInCache {
DOMString someEmptyMethod ();
DOMString someMethod (SomethingUnknown root);
USVString? optionalUsvStringFetcher(USVString name);
readonly attribute WhateverUknownParam someReadOnlyParam;
attribute WhateverUknownParam someWriteableParam;
};
@@ -1,8 +0,0 @@
public external open class SomethingNotInCache {
open val someReadOnlyParam: dynamic
var someWriteableParam: dynamic
fun someEmptyMethod(): String
fun someMethod(root: dynamic): String
fun optionalUsvStringFetcher(name: String): String?
}