Generate Kotlin/JS stdlib dependencies via dukat
This commit is contained in:
committed by
Shagen Ogandzhanian
parent
83bb07e5ac
commit
b30537de0e
@@ -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")
|
||||
}
|
||||
+41
-16
@@ -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 = "$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())
|
||||
}
|
||||
}
|
||||
@@ -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 = "$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?
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user