mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat(javac-21): add support for Java 21 in the IDE (WIP)
This commit is contained in:
@@ -46,4 +46,6 @@ dependencies {
|
||||
implementation(libs.common.antlr4)
|
||||
implementation(libs.google.gson)
|
||||
implementation(libs.google.java.format)
|
||||
|
||||
implementation(libs.misc.jackpot30)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins
|
||||
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JavacExtractClassesTask
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JavacFinalizeTask
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JavacJackpotTask
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JavacPropertiesParserTask
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JavacRenamePackageReferencesTask
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JdkSourceDownloadTask
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
|
||||
/**
|
||||
* Downloads JDK source, extracts source files for the Java compiler and patches them so that
|
||||
* they can be used on Android.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class JavacJackpotPlugin : Plugin<Project> {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Files to copy as-is from JDK source. The keys are the JDK module names and the values are
|
||||
* glob paths to the files in <module>/share/classes directory, minus the '.java' extension.
|
||||
*
|
||||
* IMPORTANT: Package names of all the classes are prefixed with `com.itsaky.androidide` in order
|
||||
* to avoid conflicts with the classes defined in the JDK.
|
||||
*/
|
||||
private val copySourceFiles = listOf(
|
||||
"java.base" to "jdk/internal/misc/PreviewFeatures",
|
||||
"java.base" to "jdk/internal/misc/MainMethodFinder",
|
||||
"java.base" to "jdk/internal/javac/*",
|
||||
)
|
||||
}
|
||||
|
||||
override fun apply(target: Project) {
|
||||
target.run {
|
||||
val javacAndroid = layout.buildDirectory.dir("javac-android")
|
||||
val jdkSource = javacAndroid.get().dir("jdk")
|
||||
|
||||
val downloadTask = tasks.register("downloadJdkSource", JdkSourceDownloadTask::class.java)
|
||||
|
||||
val deleteExtractedSourceTask = tasks.register("deleteExtractedJdkSource",
|
||||
Delete::class.java) {
|
||||
dependsOn(downloadTask)
|
||||
delete(jdkSource)
|
||||
}
|
||||
|
||||
val extractJdkSourceTask = tasks.register("extractJdkSource", Copy::class.java) {
|
||||
dependsOn(deleteExtractedSourceTask)
|
||||
|
||||
from(tarTree(downloadTask.get().outputFile))
|
||||
into(jdkSource)
|
||||
}
|
||||
|
||||
val jackpotTaskName = "javacJackpot"
|
||||
val transformedJavacSourceDir = javacAndroid.get().dir(jackpotTaskName)
|
||||
|
||||
val deleteTransformedSourcesTask = tasks.register(
|
||||
"deleteTransformedJavacSources",
|
||||
Delete::class.java
|
||||
) {
|
||||
dependsOn(extractJdkSourceTask)
|
||||
delete(transformedJavacSourceDir)
|
||||
}
|
||||
|
||||
val copiedSourceFilesDir = javacAndroid.get().dir("copiedSources")
|
||||
val copySourcesTask = tasks.register("copySourceFiles", Copy::class.java) {
|
||||
dependsOn(deleteTransformedSourcesTask)
|
||||
|
||||
copySourceFiles.forEach { (module, files) ->
|
||||
from(
|
||||
jdkSource.dir(downloadTask.get().jdkSourceDirName()).dir("src/${module}/share/classes")
|
||||
)
|
||||
include("${files}.java")
|
||||
}
|
||||
|
||||
into(copiedSourceFilesDir.dir("com/itsaky/androidide"))
|
||||
}
|
||||
|
||||
val jackpotTask = tasks.register(jackpotTaskName, JavacJackpotTask::class.java) {
|
||||
dependsOn(copySourcesTask)
|
||||
source(project.fileTree("src/main/resources").matching { include("**/*.hint") })
|
||||
jdkSourceDirectory.set(jdkSource.dir(downloadTask.get().jdkSourceDirName()))
|
||||
sourceOutDirectory.set(transformedJavacSourceDir)
|
||||
}
|
||||
|
||||
val deleteInfoFilesTask = tasks.register("deleteInfoFiles", Delete::class.java) {
|
||||
delete(project.fileTree(transformedJavacSourceDir).also {
|
||||
dependsOn(jackpotTask)
|
||||
it.include("**/module-info.java", "**/package-info.java")
|
||||
})
|
||||
}
|
||||
|
||||
val renamePackageReferencesTask = tasks.register("renamePackageReferences",
|
||||
JavacRenamePackageReferencesTask::class.java) {
|
||||
dependsOn(deleteInfoFilesTask)
|
||||
source(transformedJavacSourceDir.asFileTree.matching { include("**/*.java") })
|
||||
source(copiedSourceFilesDir)
|
||||
}
|
||||
|
||||
val compilePropertiesTask = target.tasks.register("compileProperties",
|
||||
JavacPropertiesParserTask::class.java) {
|
||||
dependsOn(renamePackageReferencesTask)
|
||||
sourceDir.set(transformedJavacSourceDir.dir("jdk.compiler"))
|
||||
outputs.dir(getPropsDir())
|
||||
}
|
||||
|
||||
val finalTask = tasks.register("finalizeJavacTask", JavacFinalizeTask::class.java) {
|
||||
dependsOn(compilePropertiesTask)
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile::class.java) {
|
||||
dependsOn(finalTask)
|
||||
|
||||
options.compilerArgs.add("--add-exports=java.base/sun.reflect.annotation=ALL-UNNAMED")
|
||||
}
|
||||
|
||||
extensions.getByType(JavaPluginExtension::class.java).run {
|
||||
sourceSets.getByName("main").java.srcDirs(
|
||||
JavacJackpotTask.getTransformedSourceDirectories(transformedJavacSourceDir.asFile),
|
||||
copiedSourceFilesDir,
|
||||
compilePropertiesTask.get().getPropsDir()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-26
@@ -41,32 +41,32 @@ class PropertiesParserPlugin : Plugin<Project> {
|
||||
sourceSets.getByName("main").java { srcDir(target.propsDir) }
|
||||
}
|
||||
|
||||
target.tasks.register("compileProperties") {
|
||||
doLast {
|
||||
val options = target.createParserOptions()
|
||||
if (options.isEmpty()) {
|
||||
return@doLast
|
||||
}
|
||||
|
||||
val parser = PropertiesParser { target.logger.lifecycle(it) }
|
||||
|
||||
logger.info(
|
||||
"Running PropertiesParser with arguments: ${options.joinToString(separator = " ")}"
|
||||
)
|
||||
|
||||
val ok = parser.run(options.toTypedArray())
|
||||
if (!ok) {
|
||||
throw GradleException("Failed to parse property files")
|
||||
}
|
||||
}
|
||||
|
||||
outputs.dir(target.propsDir)
|
||||
}
|
||||
|
||||
target.tasks.register("cleanCompiledProperties", Delete::class.java) { delete(target.propsDir) }
|
||||
|
||||
target.tasks.withType(JavaCompile::class.java) { dependsOn("compileProperties") }
|
||||
target.tasks.getByName("clean").dependsOn("cleanCompiledProperties")
|
||||
// target.tasks.register("compileProperties") {
|
||||
// doLast {
|
||||
// val options = target.createParserOptions()
|
||||
// if (options.isEmpty()) {
|
||||
// return@doLast
|
||||
// }
|
||||
//
|
||||
// val parser = PropertiesParser { target.logger.lifecycle(it) }
|
||||
//
|
||||
// logger.info(
|
||||
// "Running PropertiesParser with arguments: ${options.joinToString(separator = " ")}"
|
||||
// )
|
||||
//
|
||||
// val ok = parser.run(options.toTypedArray())
|
||||
// if (!ok) {
|
||||
// throw GradleException("Failed to parse property files")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// outputs.dir(target.propsDir)
|
||||
// }
|
||||
//
|
||||
// target.tasks.register("cleanCompiledProperties", Delete::class.java) { delete(target.propsDir) }
|
||||
//
|
||||
// target.tasks.withType(JavaCompile::class.java) { dependsOn("compileProperties") }
|
||||
// target.tasks.getByName("clean").dependsOn("cleanCompiledProperties")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ fun Project.configureAndroidModule(
|
||||
arrayOf(
|
||||
"META-INF/CHANGES",
|
||||
"META-INF/README.md",
|
||||
"nbjavac.hint"
|
||||
)
|
||||
)
|
||||
pickFirsts.addAll(
|
||||
|
||||
+5
-14
@@ -46,20 +46,11 @@ public class PropertiesParser {
|
||||
|
||||
public Logger logger;
|
||||
|
||||
public PropertiesParser(Logger logger) {
|
||||
private final File baseDir;
|
||||
|
||||
public PropertiesParser(Logger logger, File baseDir) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
boolean ok = run(args, System.out);
|
||||
if (!ok) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean run(String[] args, PrintStream out) {
|
||||
PropertiesParser pp = new PropertiesParser(out::println);
|
||||
return pp.run(args);
|
||||
this.baseDir = baseDir;
|
||||
}
|
||||
|
||||
public void info(String msg) {
|
||||
@@ -86,7 +77,7 @@ public class PropertiesParser {
|
||||
File propertyFile = new File(propertyPath);
|
||||
String prefix = propertyFile.getName().split("\\.")[0];
|
||||
MessageFile messageFile = new MessageFile(propertyFile, prefix);
|
||||
new ClassGenerator().generateFactory(messageFile, new File(outPath));
|
||||
new ClassGenerator(this.baseDir).generateFactory(messageFile, new File(outPath));
|
||||
} catch (Throwable ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
+11
-7
@@ -57,6 +57,8 @@ import java.util.stream.Stream;
|
||||
|
||||
public class ClassGenerator {
|
||||
|
||||
private final File baseDir;
|
||||
|
||||
private static final String TEMPLATE_PROPS = "#\n" +
|
||||
"# Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.\n" +
|
||||
"# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n" +
|
||||
@@ -86,10 +88,10 @@ public class ClassGenerator {
|
||||
" package {0};\\n\\\n" +
|
||||
" \\n\\\n" +
|
||||
" {1}\\n\\\n" +
|
||||
" import openjdk.tools.javac.util.JCDiagnostic.Error;\\n\\\n" +
|
||||
" import openjdk.tools.javac.util.JCDiagnostic.Warning;\\n\\\n" +
|
||||
" import openjdk.tools.javac.util.JCDiagnostic.Note;\\n\\\n" +
|
||||
" import openjdk.tools.javac.util.JCDiagnostic.Fragment;\\n\\\n" +
|
||||
" import com.itsaky.androidide.com.sun.tools.javac.util.JCDiagnostic.Error;\\n\\\n" +
|
||||
" import com.itsaky.androidide.com.sun.tools.javac.util.JCDiagnostic.Warning;\\n\\\n" +
|
||||
" import com.itsaky.androidide.com.sun.tools.javac.util.JCDiagnostic.Note;\\n\\\n" +
|
||||
" import com.itsaky.androidide.com.sun.tools.javac.util.JCDiagnostic.Fragment;\\n\\\n" +
|
||||
" \\n\\\n" +
|
||||
" public class {2} '{'\\n\\\n" +
|
||||
" {3}\\n\\\n" +
|
||||
@@ -234,6 +236,10 @@ public class ClassGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
public ClassGenerator(File baseDir) {
|
||||
this.baseDir = baseDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form the name of the toplevel factory class.
|
||||
*/
|
||||
@@ -337,9 +343,7 @@ public class ClassGenerator {
|
||||
*/
|
||||
String packageName(File file) {
|
||||
String path = file.getAbsolutePath();
|
||||
int begin = path.indexOf("src/main/resources".replace('/', File.separatorChar))
|
||||
+ "src/main/resources".length();
|
||||
String packagePath = path.substring(begin + 1, path.lastIndexOf(File.separatorChar));
|
||||
String packagePath = path.substring(baseDir.getAbsolutePath().length() + 1, path.lastIndexOf(File.separatorChar));
|
||||
return packagePath.replace(File.separatorChar, '.');
|
||||
}
|
||||
|
||||
|
||||
+18
-18
@@ -46,30 +46,30 @@ public interface MessageType {
|
||||
*/
|
||||
enum SimpleType implements MessageType {
|
||||
|
||||
ANNOTATION("annotation", "Compound", "openjdk.tools.javac.code.Attribute"),
|
||||
ANNOTATION("annotation", "Compound", "com.itsaky.androidide.com.sun.tools.javac.code.Attribute"),
|
||||
BOOLEAN("boolean", "boolean", null),
|
||||
COLLECTION("collection", "Collection", "java.util"),
|
||||
FLAG("flag", "Flag", "openjdk.tools.javac.code.Flags"),
|
||||
FLAG("flag", "Flag", "com.itsaky.androidide.com.sun.tools.javac.code.Flags"),
|
||||
FRAGMENT("fragment", "Fragment", null),
|
||||
DIAGNOSTIC("diagnostic", "JCDiagnostic", "openjdk.tools.javac.util"),
|
||||
MODIFIER("modifier", "Modifier", "jdkx.lang.model.element"),
|
||||
DIAGNOSTIC("diagnostic", "JCDiagnostic", "com.itsaky.androidide.com.sun.tools.javac.util"),
|
||||
MODIFIER("modifier", "Modifier", "com.itsaky.androidide.javax.lang.model.element"),
|
||||
FILE("file", "File", "java.io"),
|
||||
FILE_OBJECT("file object", "JavaFileObject", "jdkx.tools"),
|
||||
FILE_OBJECT("file object", "JavaFileObject", "com.itsaky.androidide.javax.tools"),
|
||||
PATH("path", "Path", "java.nio.file"),
|
||||
NAME("name", "Name", "openjdk.tools.javac.util"),
|
||||
NAME("name", "Name", "com.itsaky.androidide.com.sun.tools.javac.util"),
|
||||
NUMBER("number", "int", null),
|
||||
OPTION_NAME("option name", "Option", "openjdk.tools.javac.main"),
|
||||
PROFILE("profile", "Profile", "openjdk.tools.javac.jvm"),
|
||||
SOURCE("source", "Source", "openjdk.tools.javac.code"),
|
||||
SOURCE_VERSION("source version", "SourceVersion", "jdkx.lang.model"),
|
||||
OPTION_NAME("option name", "Option", "com.itsaky.androidide.com.sun.tools.javac.main"),
|
||||
PROFILE("profile", "Profile", "com.itsaky.androidide.com.sun.tools.javac.jvm"),
|
||||
SOURCE("source", "Source", "com.itsaky.androidide.com.sun.tools.javac.code"),
|
||||
SOURCE_VERSION("source version", "SourceVersion", "com.itsaky.androidide.javax.lang.model"),
|
||||
STRING("string", "String", null),
|
||||
SYMBOL("symbol", "Symbol", "openjdk.tools.javac.code"),
|
||||
SYMBOL_KIND("symbol kind", "Kind", "openjdk.tools.javac.code.Kinds"),
|
||||
KIND_NAME("kind name", "KindName", "openjdk.tools.javac.code.Kinds"),
|
||||
TARGET("target", "Target", "openjdk.tools.javac.jvm"),
|
||||
TOKEN("token", "TokenKind", "openjdk.tools.javac.parser.Tokens"),
|
||||
TREE_TAG("tree tag", "Tag", "openjdk.tools.javac.tree.JCTree"),
|
||||
TYPE("type", "Type", "openjdk.tools.javac.code"),
|
||||
SYMBOL("symbol", "Symbol", "com.itsaky.androidide.com.sun.tools.javac.code"),
|
||||
SYMBOL_KIND("symbol kind", "Kind", "com.itsaky.androidide.com.sun.tools.javac.code.Kinds"),
|
||||
KIND_NAME("kind name", "KindName", "com.itsaky.androidide.com.sun.tools.javac.code.Kinds"),
|
||||
TARGET("target", "Target", "com.itsaky.androidide.com.sun.tools.javac.jvm"),
|
||||
TOKEN("token", "TokenKind", "com.itsaky.androidide.com.sun.tools.javac.parser.Tokens"),
|
||||
TREE_TAG("tree tag", "Tag", "com.itsaky.androidide.com.sun.tools.javac.tree.JCTree"),
|
||||
TYPE("type", "Type", "com.itsaky.androidide.com.sun.tools.javac.code"),
|
||||
URL("url", "URL", "java.net"),
|
||||
SET("set", "Set", "java.util"),
|
||||
LIST("list", "List", "java.util"),
|
||||
@@ -114,7 +114,7 @@ public interface MessageType {
|
||||
* handy option when prototyping - but usages of custom types should be avoided in product-quality
|
||||
* resource file comments.
|
||||
* <p>
|
||||
* Example: 'openjdk.tools.javac.code.Flags.Flag'
|
||||
* Example: 'com.itsaky.androidide.com.sun.tools.javac.code.Flags.Flag'
|
||||
*/
|
||||
class CustomType implements MessageType {
|
||||
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.net.URI
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.copyTo
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JavacExtractClassesTask : DefaultTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val classFiles: ListProperty<Pair<String, String>>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun extractClasses() {
|
||||
val modules = Paths.get(URI.create("jrt:/")).resolve("/modules")
|
||||
|
||||
val outDir = outputDirectory.get().asFile
|
||||
|
||||
for ((module, classFile) in classFiles.get()) {
|
||||
val classPath = "${classFile}.class"
|
||||
val path = modules.resolve(module).resolve(classPath)
|
||||
if (!Files.exists(path)) {
|
||||
throw GradleException("Class file $classPath does not exist in module $module")
|
||||
}
|
||||
|
||||
val target = outDir.toPath().resolve(classPath)
|
||||
Files.createDirectories(target.parent)
|
||||
path.copyTo(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
/**
|
||||
* No-op, marker task.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JavacFinalizeTask : DefaultTask() {
|
||||
|
||||
@TaskAction
|
||||
fun noOp() {
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.SourceTask
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Task to transform Java compiler sources to make it compatible with the Android Runtime.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JavacJackpotTask : SourceTask() {
|
||||
|
||||
/**
|
||||
* The source directory for the JDK.
|
||||
*/
|
||||
@get:InputDirectory
|
||||
abstract val jdkSourceDirectory: DirectoryProperty
|
||||
|
||||
/**
|
||||
* The output directory where all the transformed sources will be stored.
|
||||
*/
|
||||
@get:OutputDirectory
|
||||
@get:Optional
|
||||
abstract val sourceOutDirectory: DirectoryProperty
|
||||
|
||||
init {
|
||||
run {
|
||||
sourceOutDirectory.convention(project.layout.buildDirectory.dir(name))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val javaPckRegex = """^[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*${'$'}""".toRegex()
|
||||
private val packageTransformExcludes = arrayOf(
|
||||
"sun.reflect"
|
||||
)
|
||||
|
||||
fun getTransformedSourceDirectories(sourceOutDir: File): List<File> {
|
||||
return compilerModules().map { resolveTransformedSrcDir(sourceOutDir, it, false) }
|
||||
}
|
||||
|
||||
private fun compilerModules(): Array<String> {
|
||||
return arrayOf("java.compiler", "jdk.compiler")
|
||||
}
|
||||
|
||||
private fun resolveTransformedSrcDir(srcOut: File, module: String, transformPck: Boolean) =
|
||||
srcOut.resolve(module).let {
|
||||
if (transformPck) {
|
||||
File(it, "com/itsaky/androidide")
|
||||
} else it
|
||||
}
|
||||
|
||||
private fun resolveJdkModuleJavaSrc(jdkSrc: File, module: String) =
|
||||
jdkSrc.resolve("src/${module}/share/classes")
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun jackpot() {
|
||||
val projectJavaSrc = project.file("src/main/java")
|
||||
val jdkSrc = jdkSourceDirectory.get().asFile
|
||||
val srcOut = sourceOutDirectory.get().asFile
|
||||
val modules = compilerModules()
|
||||
|
||||
for (module in modules) {
|
||||
resolveJdkModuleJavaSrc(jdkSrc, module).copyRecursively(
|
||||
resolveTransformedSrcDir(srcOut, module, true))
|
||||
}
|
||||
|
||||
val sourcePaths = mutableListOf(projectJavaSrc.absolutePath).let { paths ->
|
||||
paths.addAll(
|
||||
modules.map { module -> resolveJdkModuleJavaSrc(jdkSrc, module).absolutePath }
|
||||
)
|
||||
paths.joinToString(separator = File.pathSeparator)
|
||||
}
|
||||
|
||||
// sorted
|
||||
val finalHintFiles = source.files.map { it.absolutePath }.toMutableList()
|
||||
finalHintFiles.add(writePckTransformationsHintFile(srcOut))
|
||||
|
||||
finalHintFiles.sortByDescending { it.substringAfterLast(File.separatorChar).substring(0, 2) }
|
||||
|
||||
for (hintFile in finalHintFiles) {
|
||||
val cmdline = createCmdlineForHintFile(
|
||||
hintFile,
|
||||
sourcePaths,
|
||||
modules,
|
||||
srcOut
|
||||
)
|
||||
|
||||
logger.lifecycle("Running: ${cmdline.joinToString(separator = " ")}")
|
||||
org.netbeans.modules.jackpot30.cmdline.Main.compile(*cmdline.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCmdlineForHintFile(hintFilePath: String, sourcePaths: String,
|
||||
modules: Array<String>,
|
||||
srcOut: File): MutableList<String> {
|
||||
val cmdline = mutableListOf(
|
||||
"--hint-file",
|
||||
hintFilePath,
|
||||
"--sourcepath",
|
||||
sourcePaths,
|
||||
"--source", "8",
|
||||
"--apply"
|
||||
)
|
||||
|
||||
cmdline.addAll(modules.map { module ->
|
||||
resolveTransformedSrcDir(srcOut, module, false).also { it.mkdirs() }.absolutePath
|
||||
})
|
||||
return cmdline
|
||||
}
|
||||
|
||||
private fun writePckTransformationsHintFile(srcOut: File): String {
|
||||
val hintFileOut = sourceOutDirectory.file("ac_nb-javac-android.hint").get().asFile
|
||||
val packgeTransformations = compilerModules().flatMap { module ->
|
||||
val moduleDir = resolveTransformedSrcDir(srcOut, module, true)
|
||||
|
||||
// list all classes and change the refactor to change their package names
|
||||
// refactoring the package names directly causes strange issues and in some cases,
|
||||
// a few classes are left out
|
||||
// as a result, we refactor each class individually and change the package/import declarations
|
||||
// manually using find & replace
|
||||
moduleDir.walkBottomUp()
|
||||
.filter { it.isFile && it.extension == "java" }
|
||||
.mapNotNull {
|
||||
it.absolutePath.substring(moduleDir.absolutePath.length)
|
||||
.takeIf { path -> path.isNotBlank() }
|
||||
}
|
||||
.map { it.substring(1).replace(File.separatorChar, '.') }
|
||||
.map { it.substringBeforeLast(".java") }
|
||||
.distinct()
|
||||
|
||||
// to change the package names directly,
|
||||
// use the following
|
||||
// moduleDir.walkBottomUp()
|
||||
// .filter { it.isDirectory }
|
||||
// .mapNotNull {
|
||||
// it.absolutePath.substring(moduleDir.absolutePath.length)
|
||||
// .takeIf { path -> path.isNotBlank() }
|
||||
// }
|
||||
// .map { it.substring(1).replace(File.separatorChar, '.') }
|
||||
// .distinct()
|
||||
// .filter { pck -> javaPckRegex.matches(pck) && packageTransformExcludes.none { it == pck || it.startsWith(pck) } }
|
||||
// }.joinToString(separator = System.lineSeparator()) { pck ->
|
||||
// "$pck => com.itsaky.androidide.${pck} ;;"
|
||||
// }
|
||||
}.joinToString(separator = System.lineSeparator()) { cls ->
|
||||
"$cls => com.itsaky.androidide.${cls} ;;"
|
||||
}
|
||||
|
||||
hintFileOut.writeText(packgeTransformations)
|
||||
|
||||
return hintFileOut.absolutePath
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import com.itsaky.androidide.plugins.properties.PropertiesParser
|
||||
import com.itsaky.androidide.plugins.properties.gen.ClassGenerator
|
||||
import com.itsaky.androidide.plugins.tasks.javac.JavacPropertiesParserTask.Companion.getGeneratedPropsDirectory
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JavacPropertiesParserTask : DefaultTask() {
|
||||
|
||||
@get:Internal
|
||||
abstract val sourceDir: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun parseProperties() {
|
||||
val options = project.createParserOptions(sourceDir.get().asFile)
|
||||
if (options.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val parser = PropertiesParser({ logger.lifecycle(it) }, sourceDir.get().asFile)
|
||||
|
||||
logger.info(
|
||||
"Running PropertiesParser with arguments: ${options.joinToString(separator = " ")}"
|
||||
)
|
||||
|
||||
val ok = parser.run(options.toTypedArray())
|
||||
if (!ok) {
|
||||
throw GradleException("Failed to parse property files")
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
fun getPropsDir(): File {
|
||||
return project.getGeneratedPropsDirectory()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun Project.getGeneratedPropsDirectory(): File =
|
||||
project.layout.buildDirectory.dir("generated/properties").get().asFile
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createParserOptions(baseDir: File): List<String> {
|
||||
val options = mutableListOf<String>()
|
||||
baseDir.walkTopDown().forEach {
|
||||
if (!(it.isFile && it.extension == "properties")) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val destPath =
|
||||
it.path.substringAfter(baseDir.absolutePath)
|
||||
.substringBeforeLast(File.separatorChar) +
|
||||
File.separator +
|
||||
ClassGenerator.toplevelName(it) +
|
||||
".java"
|
||||
|
||||
val destFile = File(getGeneratedPropsDirectory(), destPath)
|
||||
|
||||
if (destFile.exists() && destFile.lastModified() >= it.lastModified()) {
|
||||
logger.info("${it.name} is up-to-date. Skipping.")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
destFile.parentFile.mkdirs()
|
||||
options.add("-compile")
|
||||
options.add(it.absolutePath)
|
||||
options.add(destFile.parentFile.absolutePath)
|
||||
}
|
||||
return options
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import org.gradle.api.tasks.SourceTask
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JavacRenamePackageReferencesTask : SourceTask() {
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@TaskAction
|
||||
fun doRenameReferences() {
|
||||
val workQueue = workerExecutor.noIsolation()
|
||||
for (file in source.files) {
|
||||
workQueue.submit(JavacRenamePackgeReferencesWorker::class.java) {
|
||||
this.file.set(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JavacRenamePackgeReferencesWorker: WorkAction<JavacRenamePackgeReferencesWorker.Params> {
|
||||
|
||||
companion object {
|
||||
|
||||
private val unsafeToReplacePackages = arrayOf(
|
||||
"com.sun",
|
||||
"jdk.internal",
|
||||
"sun.tools"
|
||||
)
|
||||
|
||||
private val safeToReplacePackages = arrayOf(
|
||||
"javax.annotation",
|
||||
"javax.lang",
|
||||
"javax.tools",
|
||||
)
|
||||
|
||||
private val replaceTypes = arrayOf(
|
||||
"package",
|
||||
"import",
|
||||
"import static"
|
||||
)
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val file = parameters.file.get().asFile
|
||||
var content = file.readText()
|
||||
|
||||
for (pck in unsafeToReplacePackages) {
|
||||
for (typ in replaceTypes) {
|
||||
content = content.replace(Regex.fromLiteral("$typ $pck"), "$typ com.itsaky.androidide.$pck")
|
||||
}
|
||||
}
|
||||
|
||||
for (pck in safeToReplacePackages) {
|
||||
content = content.replace(Regex.fromLiteral(pck), "com.itsaky.androidide.$pck")
|
||||
}
|
||||
|
||||
file.writeText(content)
|
||||
}
|
||||
|
||||
interface Params: WorkParameters {
|
||||
val file: RegularFileProperty
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.plugins.tasks.javac
|
||||
|
||||
import com.itsaky.androidide.plugins.util.DownloadUtils
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
/**
|
||||
* Task to download JDK source code.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class JdkSourceDownloadTask : DefaultTask() {
|
||||
|
||||
/**
|
||||
* The commit hash to download.
|
||||
*/
|
||||
@get:Input
|
||||
abstract val commitHash: Property<String>
|
||||
|
||||
/**
|
||||
* The SHA-256 hash of the source tarball.
|
||||
*/
|
||||
@get:Input
|
||||
abstract val sourceSHA256: Property<String>
|
||||
|
||||
/**
|
||||
* The directory to download the source tarbar to.
|
||||
*/
|
||||
@get:OutputFile
|
||||
@get:Optional
|
||||
abstract val outputFile: RegularFileProperty
|
||||
|
||||
init {
|
||||
run {
|
||||
commitHash.convention(DEFAULT_JDK_COMMIT_HASH)
|
||||
sourceSHA256.convention(DEFAULT_JDK_SOURCE_SHA)
|
||||
outputFile.convention(project.layout.buildDirectory.dir(name).get().file("${commitHash.get()}.tar.gz"))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DEFAULT_JDK_COMMIT_HASH = "3036d3921cae2900aab4610b1e22ee816ac64e2d"
|
||||
private const val DEFAULT_JDK_SOURCE_SHA = "d6d9aa6465becf1332bdca6d60cbf37f66ac0d2e7755c3197125d452f03ec657"
|
||||
private const val REPOSITORY_OWNER = "itsaky"
|
||||
private const val REPOSITORY_NAME = "openjdk-21-android"
|
||||
private const val JDK_DOWNLOAD_URL = "https://github.com/${REPOSITORY_OWNER}/${REPOSITORY_NAME}/archive/%s.tar.gz"
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun downloadSource() {
|
||||
val downloadUrl = JDK_DOWNLOAD_URL.format(commitHash.get())
|
||||
val outFile = outputFile.get().asFile
|
||||
|
||||
DownloadUtils.doDownload(outFile, downloadUrl, sourceSHA256.get(), logger)
|
||||
|
||||
if (!outFile.exists() || !outFile.isFile) {
|
||||
throw GradleException("$outFile does not exist or is not a file.")
|
||||
}
|
||||
}
|
||||
|
||||
fun jdkSourceDirName() : String {
|
||||
return "${REPOSITORY_NAME}-${commitHash.get()}"
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ object DownloadUtils {
|
||||
logger: Logger
|
||||
) {
|
||||
|
||||
logger.info("Download $remoteUrl to $file having checksum ${expectedChecksum}...")
|
||||
logger.lifecycle("Download $remoteUrl to $file having checksum ${expectedChecksum}...")
|
||||
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ maven-publish-plugin = "0.25.3"
|
||||
[libraries]
|
||||
|
||||
# AndroidIDE
|
||||
androidide-javac = { module = "io.github.itsaky:nb-javac-android", version = "17.0.0.4-SNAPSHOT" }
|
||||
androidide-ts = { module = "com.itsaky.androidide.treesitter:android-tree-sitter", version.ref = "tree-sitter" }
|
||||
androidide-ts-java = { module = "com.itsaky.androidide.treesitter:tree-sitter-java", version.ref = "tree-sitter" }
|
||||
androidide-ts-json = { module = "com.itsaky.androidide.treesitter:tree-sitter-json", version.ref = "tree-sitter" }
|
||||
@@ -144,6 +143,9 @@ kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", v
|
||||
nav-safe-args-gradle-plugin = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "androidx-navigation" }
|
||||
maven-publish = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "maven-publish-plugin" }
|
||||
|
||||
# Misc
|
||||
misc-jackpot30 = { module = "org.apache.netbeans.modules.jackpot30:tool", version = "13.0" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
include(":subprojects:javac-deps")
|
||||
|
||||
|
||||
include(":subprojects:javac-jackpot")
|
||||
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
pluginManagement {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@Suppress("")
|
||||
plugins {
|
||||
id("java-library")
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
+712
@@ -0,0 +1,712 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.sun.reflect.annotation;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import sun.reflect.annotation.AnnotationType;
|
||||
|
||||
/**
|
||||
* InvocationHandler for dynamic proxy implementation of Annotation.
|
||||
*
|
||||
* @author Josh Bloch
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("Since15")
|
||||
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 6182022883658399397L;
|
||||
private final Class<? extends Annotation> type;
|
||||
@SuppressWarnings("serial") // Not statically typed as Serializable
|
||||
private final Map<String, Object> memberValues;
|
||||
|
||||
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
|
||||
Class<?>[] superInterfaces = type.getInterfaces();
|
||||
if (!type.isAnnotation() ||
|
||||
superInterfaces.length != 1 ||
|
||||
superInterfaces[0] != Annotation.class)
|
||||
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type: " +
|
||||
type.getName());
|
||||
this.type = type;
|
||||
this.memberValues = memberValues;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
String member = method.getName();
|
||||
int parameterCount = method.getParameterCount();
|
||||
|
||||
// Handle Object and Annotation methods
|
||||
if (parameterCount == 1 && member == "equals" &&
|
||||
method.getParameterTypes()[0] == Object.class) {
|
||||
return equalsImpl(proxy, args[0]);
|
||||
}
|
||||
if (parameterCount != 0) {
|
||||
throw new AssertionError("Too many parameters for an annotation method");
|
||||
}
|
||||
|
||||
if (member == "toString") {
|
||||
return toStringImpl();
|
||||
} else if (member == "hashCode") {
|
||||
return hashCodeImpl();
|
||||
} else if (member == "annotationType") {
|
||||
return type;
|
||||
}
|
||||
|
||||
// Handle annotation member accessors
|
||||
Object result = memberValues.get(member);
|
||||
|
||||
if (result == null)
|
||||
throw new IncompleteAnnotationException(type, member);
|
||||
|
||||
if (result instanceof ExceptionProxy)
|
||||
throw ((ExceptionProxy) result).generateException();
|
||||
|
||||
if (result.getClass().isArray() && Array.getLength(result) != 0)
|
||||
result = cloneArray(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method, which clones its array argument, would not be necessary
|
||||
* if Cloneable had a public clone method.
|
||||
*/
|
||||
private Object cloneArray(Object array) {
|
||||
Class<?> type = array.getClass();
|
||||
|
||||
if (type == byte[].class) {
|
||||
byte[] byteArray = (byte[])array;
|
||||
return byteArray.clone();
|
||||
}
|
||||
if (type == char[].class) {
|
||||
char[] charArray = (char[])array;
|
||||
return charArray.clone();
|
||||
}
|
||||
if (type == double[].class) {
|
||||
double[] doubleArray = (double[])array;
|
||||
return doubleArray.clone();
|
||||
}
|
||||
if (type == float[].class) {
|
||||
float[] floatArray = (float[])array;
|
||||
return floatArray.clone();
|
||||
}
|
||||
if (type == int[].class) {
|
||||
int[] intArray = (int[])array;
|
||||
return intArray.clone();
|
||||
}
|
||||
if (type == long[].class) {
|
||||
long[] longArray = (long[])array;
|
||||
return longArray.clone();
|
||||
}
|
||||
if (type == short[].class) {
|
||||
short[] shortArray = (short[])array;
|
||||
return shortArray.clone();
|
||||
}
|
||||
if (type == boolean[].class) {
|
||||
boolean[] booleanArray = (boolean[])array;
|
||||
return booleanArray.clone();
|
||||
}
|
||||
|
||||
Object[] objectArray = (Object[])array;
|
||||
return objectArray.clone();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of dynamicProxy.toString()
|
||||
*/
|
||||
private String toStringImpl() {
|
||||
StringBuilder result = new StringBuilder(128);
|
||||
result.append('@');
|
||||
// Guard against null canonical name; shouldn't happen
|
||||
result.append(Objects.toString(type.getCanonicalName(),
|
||||
"<no canonical name>"));
|
||||
result.append('(');
|
||||
boolean firstMember = true;
|
||||
Set<Map.Entry<String, Object>> entries = memberValues.entrySet();
|
||||
boolean loneValue = entries.size() == 1;
|
||||
for (Map.Entry<String, Object> e : entries) {
|
||||
if (firstMember)
|
||||
firstMember = false;
|
||||
else
|
||||
result.append(", ");
|
||||
|
||||
String key = e.getKey();
|
||||
if (!loneValue || !"value".equals(key)) {
|
||||
result.append(key);
|
||||
result.append('=');
|
||||
}
|
||||
loneValue = false;
|
||||
result.append(memberValueToString(e.getValue()));
|
||||
}
|
||||
result.append(')');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a member value (in "dynamic proxy return form") into a string.
|
||||
*/
|
||||
private static String memberValueToString(Object value) {
|
||||
Class<?> type = value.getClass();
|
||||
if (!type.isArray()) {
|
||||
// primitive value, string, class, enum const, or annotation
|
||||
if (type == Class.class)
|
||||
return toSourceString((Class<?>) value);
|
||||
else if (type == String.class)
|
||||
return toSourceString((String) value);
|
||||
if (type == Character.class)
|
||||
return toSourceString((char) value);
|
||||
else if (type == Double.class)
|
||||
return toSourceString((double) value);
|
||||
else if (type == Float.class)
|
||||
return toSourceString((float) value);
|
||||
else if (type == Long.class)
|
||||
return toSourceString((long) value);
|
||||
else if (type == Byte.class)
|
||||
return toSourceString((byte) value);
|
||||
else if (value instanceof Enum<?>)
|
||||
// Predicate above covers enum constants, including
|
||||
// those with specialized class bodies.
|
||||
return toSourceString(((Enum<?>) value));
|
||||
else
|
||||
return value.toString();
|
||||
} else {
|
||||
Stream<String> stringStream;
|
||||
if (type == byte[].class)
|
||||
stringStream = convert((byte[]) value);
|
||||
else if (type == char[].class)
|
||||
stringStream = convert((char[]) value);
|
||||
else if (type == double[].class)
|
||||
stringStream = DoubleStream.of((double[]) value)
|
||||
.mapToObj(AnnotationInvocationHandler::toSourceString);
|
||||
else if (type == float[].class)
|
||||
stringStream = convert((float[]) value);
|
||||
else if (type == int[].class)
|
||||
stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf);
|
||||
else if (type == long[].class) {
|
||||
stringStream = LongStream.of((long[]) value)
|
||||
.mapToObj(AnnotationInvocationHandler::toSourceString);
|
||||
} else if (type == short[].class)
|
||||
stringStream = convert((short[]) value);
|
||||
else if (type == boolean[].class)
|
||||
stringStream = convert((boolean[]) value);
|
||||
else if (type == Class[].class)
|
||||
stringStream =
|
||||
Arrays.stream((Class<?>[]) value).
|
||||
map(AnnotationInvocationHandler::toSourceString);
|
||||
else if (type == String[].class)
|
||||
stringStream =
|
||||
Arrays.stream((String[])value).
|
||||
map(AnnotationInvocationHandler::toSourceString);
|
||||
else if (type.getComponentType().isEnum())
|
||||
stringStream =
|
||||
Arrays.stream((Enum<?>[])value).
|
||||
map(AnnotationInvocationHandler::toSourceString);
|
||||
else
|
||||
stringStream = Arrays.stream((Object[])value).map(Objects::toString);
|
||||
|
||||
return stringStreamToString(stringStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a Class value to a form suitable for use in the
|
||||
* string representation of an annotation.
|
||||
*/
|
||||
private static String toSourceString(Class<?> clazz) {
|
||||
// Guard against null canonical name; shouldn't happen
|
||||
return Objects.toString(clazz.getCanonicalName(),
|
||||
"<no canonical name>") + ".class";
|
||||
}
|
||||
|
||||
private static String toSourceString(float f) {
|
||||
if (Float.isFinite(f))
|
||||
return Float.toString(f) + "f" ;
|
||||
else {
|
||||
if (Float.isInfinite(f)) {
|
||||
return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f";
|
||||
} else
|
||||
return "0.0f/0.0f";
|
||||
}
|
||||
}
|
||||
|
||||
private static String toSourceString(double d) {
|
||||
if (Double.isFinite(d))
|
||||
return Double.toString(d);
|
||||
else {
|
||||
if (Double.isInfinite(d)) {
|
||||
return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0";
|
||||
} else
|
||||
return "0.0/0.0";
|
||||
}
|
||||
}
|
||||
|
||||
private static String toSourceString(char c) {
|
||||
StringBuilder sb = new StringBuilder(4);
|
||||
sb.append('\'');
|
||||
sb.append(quote(c));
|
||||
return sb.append('\'') .toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a character if it has an escape sequence or is
|
||||
* non-printable ASCII. Leaves non-ASCII characters alone.
|
||||
*/
|
||||
private static String quote(char ch) {
|
||||
switch (ch) {
|
||||
case '\b': return "\\b";
|
||||
case '\f': return "\\f";
|
||||
case '\n': return "\\n";
|
||||
case '\r': return "\\r";
|
||||
case '\t': return "\\t";
|
||||
case '\'': return "\\'";
|
||||
case '\"': return "\\\"";
|
||||
case '\\': return "\\\\";
|
||||
default:
|
||||
return (isPrintableAscii(ch))
|
||||
? String.valueOf(ch)
|
||||
: String.format("\\u%04x", (int) ch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a character printable ASCII?
|
||||
*/
|
||||
private static boolean isPrintableAscii(char ch) {
|
||||
return ch >= ' ' && ch <= '~';
|
||||
}
|
||||
|
||||
private static String toSourceString(byte b) {
|
||||
return String.format("(byte)0x%02x", b);
|
||||
}
|
||||
|
||||
private static String toSourceString(long ell) {
|
||||
return String.valueOf(ell) + "L";
|
||||
}
|
||||
|
||||
private static String toSourceString(Enum<?> enumConstant) {
|
||||
return enumConstant.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string suitable for use in the string representation
|
||||
* of an annotation.
|
||||
*/
|
||||
private static String toSourceString(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('"');
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
sb.append(quote(s.charAt(i)));
|
||||
}
|
||||
sb.append('"');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static Stream<String> convert(byte[] values) {
|
||||
List<String> list = new ArrayList<>(values.length);
|
||||
for (byte b : values)
|
||||
list.add(toSourceString(b));
|
||||
return list.stream();
|
||||
}
|
||||
|
||||
private static Stream<String> convert(char[] values) {
|
||||
List<String> list = new ArrayList<>(values.length);
|
||||
for (char c : values)
|
||||
list.add(toSourceString(c));
|
||||
return list.stream();
|
||||
}
|
||||
|
||||
private static Stream<String> convert(float[] values) {
|
||||
List<String> list = new ArrayList<>(values.length);
|
||||
for (float f : values) {
|
||||
list.add(toSourceString(f));
|
||||
}
|
||||
return list.stream();
|
||||
}
|
||||
|
||||
private static Stream<String> convert(short[] values) {
|
||||
List<String> list = new ArrayList<>(values.length);
|
||||
for (short s : values)
|
||||
list.add(Short.toString(s));
|
||||
return list.stream();
|
||||
}
|
||||
|
||||
private static Stream<String> convert(boolean[] values) {
|
||||
List<String> list = new ArrayList<>(values.length);
|
||||
for (boolean b : values)
|
||||
list.add(Boolean.toString(b));
|
||||
return list.stream();
|
||||
}
|
||||
|
||||
private static String stringStreamToString(Stream<String> stream) {
|
||||
return stream.collect(Collectors.joining(", ", "{", "}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dynamicProxy.equals(Object o)
|
||||
*/
|
||||
private Boolean equalsImpl(Object proxy, Object o) {
|
||||
if (o == proxy)
|
||||
return true;
|
||||
|
||||
if (!type.isInstance(o))
|
||||
return false;
|
||||
for (Method memberMethod : getMemberMethods()) {
|
||||
if (memberMethod.isSynthetic())
|
||||
continue;
|
||||
String member = memberMethod.getName();
|
||||
Object ourValue = memberValues.get(member);
|
||||
Object hisValue = null;
|
||||
AnnotationInvocationHandler hisHandler = asOneOfUs(o);
|
||||
if (hisHandler != null) {
|
||||
hisValue = hisHandler.memberValues.get(member);
|
||||
} else {
|
||||
try {
|
||||
hisValue = memberMethod.invoke(o);
|
||||
} catch (InvocationTargetException e) {
|
||||
return false;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
if (!memberValueEquals(ourValue, hisValue))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object's invocation handler if that object is a dynamic
|
||||
* proxy with a handler of type AnnotationInvocationHandler.
|
||||
* Returns null otherwise.
|
||||
*/
|
||||
private AnnotationInvocationHandler asOneOfUs(Object o) {
|
||||
if (Proxy.isProxyClass(o.getClass())) {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(o);
|
||||
if (handler instanceof AnnotationInvocationHandler)
|
||||
return ((AnnotationInvocationHandler) handler);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff the two member values in "dynamic proxy return form"
|
||||
* are equal using the appropriate equality function depending on the
|
||||
* member type. The two values will be of the same type unless one of
|
||||
* the containing annotations is ill-formed. If one of the containing
|
||||
* annotations is ill-formed, this method will return false unless the
|
||||
* two members are identical object references.
|
||||
*/
|
||||
private static boolean memberValueEquals(Object v1, Object v2) {
|
||||
Class<?> type = v1.getClass();
|
||||
|
||||
// Check for primitive, string, class, enum const, annotation,
|
||||
// or ExceptionProxy
|
||||
if (!type.isArray())
|
||||
return v1.equals(v2);
|
||||
|
||||
// Check for array of string, class, enum const, annotation,
|
||||
// or ExceptionProxy
|
||||
if (v1 instanceof Object[] && v2 instanceof Object[])
|
||||
return Arrays.equals((Object[]) v1, (Object[]) v2);
|
||||
|
||||
// Check for ill formed annotation(s)
|
||||
if (v2.getClass() != type)
|
||||
return false;
|
||||
|
||||
// Deal with array of primitives
|
||||
if (type == byte[].class)
|
||||
return Arrays.equals((byte[]) v1, (byte[]) v2);
|
||||
if (type == char[].class)
|
||||
return Arrays.equals((char[]) v1, (char[]) v2);
|
||||
if (type == double[].class)
|
||||
return Arrays.equals((double[]) v1, (double[]) v2);
|
||||
if (type == float[].class)
|
||||
return Arrays.equals((float[]) v1, (float[]) v2);
|
||||
if (type == int[].class)
|
||||
return Arrays.equals((int[]) v1, (int[]) v2);
|
||||
if (type == long[].class)
|
||||
return Arrays.equals((long[]) v1, (long[]) v2);
|
||||
if (type == short[].class)
|
||||
return Arrays.equals((short[]) v1, (short[]) v2);
|
||||
assert type == boolean[].class;
|
||||
return Arrays.equals((boolean[]) v1, (boolean[]) v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the member methods for our annotation type. These are
|
||||
* obtained lazily and cached, as they're expensive to obtain
|
||||
* and we only need them if our equals method is invoked (which should
|
||||
* be rare).
|
||||
*/
|
||||
private Method[] getMemberMethods() {
|
||||
Method[] value = memberMethods;
|
||||
if (value == null) {
|
||||
value = computeMemberMethods();
|
||||
memberMethods = value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private Method[] computeMemberMethods() {
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<Method[]>() {
|
||||
public Method[] run() {
|
||||
final Method[] methods = type.getDeclaredMethods();
|
||||
validateAnnotationMethods(methods);
|
||||
AccessibleObject.setAccessible(methods, true);
|
||||
return methods;
|
||||
}});
|
||||
}
|
||||
|
||||
private transient volatile Method[] memberMethods;
|
||||
|
||||
/**
|
||||
* Validates that a method is structurally appropriate for an
|
||||
* annotation type. As of Java SE 8, annotation types cannot
|
||||
* contain static methods and the declared methods of an
|
||||
* annotation type must take zero arguments and there are
|
||||
* restrictions on the return type.
|
||||
*/
|
||||
private void validateAnnotationMethods(Method[] memberMethods) {
|
||||
/*
|
||||
* Specification citations below are from JLS
|
||||
* 9.6.1. Annotation Type Elements
|
||||
*/
|
||||
boolean valid = true;
|
||||
Method currentMethod = null;
|
||||
for(Method method : memberMethods) {
|
||||
currentMethod = method;
|
||||
int modifiers = method.getModifiers();
|
||||
// Skip over methods that may be a static initializer or
|
||||
// similar construct. A static initializer may be used for
|
||||
// purposes such as initializing a lambda stored in an
|
||||
// interface field.
|
||||
if (method.isSynthetic() &&
|
||||
(modifiers & (Modifier.STATIC | Modifier.PRIVATE)) != 0 &&
|
||||
method.getParameterCount() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* "By virtue of the AnnotationTypeElementDeclaration
|
||||
* production, a method declaration in an annotation type
|
||||
* declaration cannot have formal parameters, type
|
||||
* parameters, or a throws clause.
|
||||
*
|
||||
* "By virtue of the AnnotationTypeElementModifier
|
||||
* production, a method declaration in an annotation type
|
||||
* declaration cannot be default or static."
|
||||
*/
|
||||
if (modifiers != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
|
||||
method.isDefault() ||
|
||||
method.getParameterCount() != 0 ||
|
||||
method.getExceptionTypes().length != 0) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* "It is a compile-time error if the return type of a
|
||||
* method declared in an annotation type is not one of the
|
||||
* following: a primitive type, String, Class, any
|
||||
* parameterized invocation of Class, an enum type
|
||||
* (section 8.9), an annotation type, or an array type
|
||||
* (chapter 10) whose element type is one of the preceding
|
||||
* types."
|
||||
*/
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType.isArray()) {
|
||||
returnType = returnType.getComponentType();
|
||||
if (returnType.isArray()) { // Only single dimensional arrays
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!((returnType.isPrimitive() && returnType != void.class) ||
|
||||
returnType == String.class ||
|
||||
returnType == Class.class ||
|
||||
returnType.isEnum() ||
|
||||
returnType.isAnnotation())) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* "It is a compile-time error if any method declared in an
|
||||
* annotation type has a signature that is
|
||||
* override-equivalent to that of any public or protected
|
||||
* method declared in class Object or in the interface
|
||||
* java.lang.annotation.Annotation."
|
||||
*
|
||||
* The methods in Object or Annotation meeting the other
|
||||
* criteria (no arguments, constrained return type, etc.)
|
||||
* above are:
|
||||
*
|
||||
* String toString()
|
||||
* int hashCode()
|
||||
* Class<? extends Annotation> annotationType()
|
||||
*/
|
||||
String methodName = method.getName();
|
||||
if ((methodName.equals("toString") && returnType == String.class) ||
|
||||
(methodName.equals("hashCode") && returnType == int.class) ||
|
||||
(methodName.equals("annotationType") && returnType == Class.class)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
return;
|
||||
else
|
||||
throw new AnnotationFormatError("Malformed method on an annotation type: " +
|
||||
currentMethod.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dynamicProxy.hashCode()
|
||||
*/
|
||||
private int hashCodeImpl() {
|
||||
int result = 0;
|
||||
for (Map.Entry<String, Object> e : memberValues.entrySet()) {
|
||||
result += (127 * e.getKey().hashCode()) ^
|
||||
memberValueHashCode(e.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes hashCode of a member value (in "dynamic proxy return form")
|
||||
*/
|
||||
private static int memberValueHashCode(Object value) {
|
||||
Class<?> type = value.getClass();
|
||||
if (!type.isArray()) // primitive, string, class, enum const,
|
||||
// or annotation
|
||||
return value.hashCode();
|
||||
|
||||
if (type == byte[].class)
|
||||
return Arrays.hashCode((byte[]) value);
|
||||
if (type == char[].class)
|
||||
return Arrays.hashCode((char[]) value);
|
||||
if (type == double[].class)
|
||||
return Arrays.hashCode((double[]) value);
|
||||
if (type == float[].class)
|
||||
return Arrays.hashCode((float[]) value);
|
||||
if (type == int[].class)
|
||||
return Arrays.hashCode((int[]) value);
|
||||
if (type == long[].class)
|
||||
return Arrays.hashCode((long[]) value);
|
||||
if (type == short[].class)
|
||||
return Arrays.hashCode((short[]) value);
|
||||
if (type == boolean[].class)
|
||||
return Arrays.hashCode((boolean[]) value);
|
||||
return Arrays.hashCode((Object[]) value);
|
||||
}
|
||||
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws java.io.IOException, ClassNotFoundException {
|
||||
ObjectInputStream.GetField fields = s.readFields();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null);
|
||||
|
||||
// Check to make sure that types have not evolved incompatibly
|
||||
|
||||
AnnotationType annotationType = null;
|
||||
try {
|
||||
annotationType = AnnotationType.getInstance(t);
|
||||
} catch(IllegalArgumentException e) {
|
||||
// Class is no longer an annotation type; time to punch out
|
||||
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
|
||||
}
|
||||
|
||||
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
|
||||
// consistent with runtime Map type
|
||||
Map<String, Object> mv = new LinkedHashMap<>();
|
||||
|
||||
// If there are annotation members without values, that
|
||||
// situation is handled by the invoke method.
|
||||
for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {
|
||||
String name = memberValue.getKey();
|
||||
Object value = null;
|
||||
Class<?> memberType = memberTypes.get(name);
|
||||
if (memberType != null) { // i.e. member still exists
|
||||
value = memberValue.getValue();
|
||||
if (!(memberType.isInstance(value) ||
|
||||
value instanceof ExceptionProxy)) {
|
||||
value = new AnnotationTypeMismatchExceptionProxy(
|
||||
toIdentityString(value))
|
||||
.setMember(annotationType.members().get(name));
|
||||
}
|
||||
}
|
||||
mv.put(name, value);
|
||||
}
|
||||
|
||||
UnsafeAccessor.setType(this, t);
|
||||
UnsafeAccessor.setMemberValues(this, mv);
|
||||
}
|
||||
|
||||
public static String toIdentityString(Object o) {
|
||||
Objects.requireNonNull(o);
|
||||
return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an unmodifiable view on the member values.
|
||||
*/
|
||||
Map<String, Object> memberValues() {
|
||||
return Collections.unmodifiableMap(memberValues);
|
||||
}
|
||||
|
||||
private static class UnsafeAccessor {
|
||||
private static final jdk.internal.misc.Unsafe unsafe
|
||||
= jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long typeOffset = unsafe.objectFieldOffset
|
||||
(AnnotationInvocationHandler.class, "type");
|
||||
private static final long memberValuesOffset = unsafe.objectFieldOffset
|
||||
(AnnotationInvocationHandler.class, "memberValues");
|
||||
|
||||
static void setType(AnnotationInvocationHandler o,
|
||||
Class<? extends Annotation> type) {
|
||||
unsafe.putReference(o, typeOffset, type);
|
||||
}
|
||||
|
||||
static void setMemberValues(AnnotationInvocationHandler o,
|
||||
Map<String, Object> memberValues) {
|
||||
unsafe.putReference(o, memberValuesOffset, memberValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
+872
@@ -0,0 +1,872 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.sun.reflect.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import jdk.internal.reflect.ConstantPool;
|
||||
|
||||
import sun.reflect.annotation.AnnotationType;
|
||||
import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy;
|
||||
import sun.reflect.annotation.ExceptionProxy;
|
||||
import sun.reflect.annotation.TypeNotPresentExceptionProxy;
|
||||
import sun.reflect.generics.parser.SignatureParser;
|
||||
import sun.reflect.generics.tree.TypeSignature;
|
||||
import sun.reflect.generics.factory.GenericsFactory;
|
||||
import sun.reflect.generics.factory.CoreReflectionFactory;
|
||||
import sun.reflect.generics.visitor.Reifier;
|
||||
import sun.reflect.generics.scope.ClassScope;
|
||||
|
||||
/**
|
||||
* Parser for Java programming language annotations. Translates
|
||||
* annotation byte streams emitted by compiler into annotation objects.
|
||||
*
|
||||
* @author Josh Bloch
|
||||
* @since 1.5
|
||||
*/
|
||||
public class AnnotationParser {
|
||||
/**
|
||||
* Parses the annotations described by the specified byte array.
|
||||
* resolving constant references in the specified constant pool.
|
||||
* The array must contain an array of annotations as described
|
||||
* in the RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* u2 num_annotations;
|
||||
* annotation annotations[num_annotations];
|
||||
*
|
||||
* @throws AnnotationFormatError if an annotation is found to be
|
||||
* malformed.
|
||||
*/
|
||||
public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(
|
||||
byte[] rawAnnotations,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
if (rawAnnotations == null)
|
||||
return Collections.emptyMap();
|
||||
|
||||
try {
|
||||
return parseAnnotations2(rawAnnotations, constPool, container, null);
|
||||
} catch(BufferUnderflowException e) {
|
||||
throw new AnnotationFormatError("Unexpected end of annotations.");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// Type mismatch in constant pool
|
||||
throw new AnnotationFormatError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #parseAnnotations(byte[], sun.reflect.ConstantPool, Class)}
|
||||
* with an additional parameter {@code selectAnnotationClasses} which selects the
|
||||
* annotation types to parse (other than selected are quickly skipped).<p>
|
||||
* This method is used to parse select meta annotations in the construction
|
||||
* phase of {@link AnnotationType} instances to prevent infinite recursion.
|
||||
*
|
||||
* @param selectAnnotationClasses an array of annotation types to select when parsing
|
||||
*/
|
||||
@SafeVarargs
|
||||
@SuppressWarnings("varargs") // selectAnnotationClasses is used safely
|
||||
public static Map<Class<? extends Annotation>, Annotation> parseSelectAnnotations(
|
||||
byte[] rawAnnotations,
|
||||
ConstantPool constPool,
|
||||
Class<?> container,
|
||||
Class<? extends Annotation> ... selectAnnotationClasses) {
|
||||
if (rawAnnotations == null)
|
||||
return Collections.emptyMap();
|
||||
|
||||
try {
|
||||
return parseAnnotations2(rawAnnotations, constPool, container, selectAnnotationClasses);
|
||||
} catch(BufferUnderflowException e) {
|
||||
throw new AnnotationFormatError("Unexpected end of annotations.");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// Type mismatch in constant pool
|
||||
throw new AnnotationFormatError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(
|
||||
byte[] rawAnnotations,
|
||||
ConstantPool constPool,
|
||||
Class<?> container,
|
||||
Class<? extends Annotation>[] selectAnnotationClasses) {
|
||||
Map<Class<? extends Annotation>, Annotation> result =
|
||||
new LinkedHashMap<Class<? extends Annotation>, Annotation>();
|
||||
ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
|
||||
int numAnnotations = buf.getShort() & 0xFFFF;
|
||||
for (int i = 0; i < numAnnotations; i++) {
|
||||
Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses);
|
||||
if (a != null) {
|
||||
Class<? extends Annotation> klass = a.annotationType();
|
||||
if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME &&
|
||||
result.put(klass, a) != null) {
|
||||
throw new AnnotationFormatError(
|
||||
"Duplicate annotation for class: "+klass+": " + a);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the parameter annotations described by the specified byte array.
|
||||
* resolving constant references in the specified constant pool.
|
||||
* The array must contain an array of annotations as described
|
||||
* in the RuntimeVisibleParameterAnnotations_attribute:
|
||||
*
|
||||
* u1 num_parameters;
|
||||
* {
|
||||
* u2 num_annotations;
|
||||
* annotation annotations[num_annotations];
|
||||
* } parameter_annotations[num_parameters];
|
||||
*
|
||||
* Unlike parseAnnotations, rawAnnotations must not be null!
|
||||
* A null value must be handled by the caller. This is so because
|
||||
* we cannot determine the number of parameters if rawAnnotations
|
||||
* is null. Also, the caller should check that the number
|
||||
* of parameters indicated by the return value of this method
|
||||
* matches the actual number of method parameters. A mismatch
|
||||
* indicates that an AnnotationFormatError should be thrown.
|
||||
*
|
||||
* @throws AnnotationFormatError if an annotation is found to be
|
||||
* malformed.
|
||||
*/
|
||||
public static Annotation[][] parseParameterAnnotations(
|
||||
byte[] rawAnnotations,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
try {
|
||||
return parseParameterAnnotations2(rawAnnotations, constPool, container);
|
||||
} catch(BufferUnderflowException e) {
|
||||
throw new AnnotationFormatError(
|
||||
"Unexpected end of parameter annotations.");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// Type mismatch in constant pool
|
||||
throw new AnnotationFormatError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Annotation[][] parseParameterAnnotations2(
|
||||
byte[] rawAnnotations,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
|
||||
int numParameters = buf.get() & 0xFF;
|
||||
Annotation[][] result = new Annotation[numParameters][];
|
||||
|
||||
for (int i = 0; i < numParameters; i++) {
|
||||
int numAnnotations = buf.getShort() & 0xFFFF;
|
||||
List<Annotation> annotations =
|
||||
new ArrayList<Annotation>(numAnnotations);
|
||||
for (int j = 0; j < numAnnotations; j++) {
|
||||
Annotation a = parseAnnotation(buf, constPool, container, false);
|
||||
if (a != null) {
|
||||
AnnotationType type = AnnotationType.getInstance(
|
||||
a.annotationType());
|
||||
if (type.retention() == RetentionPolicy.RUNTIME)
|
||||
annotations.add(a);
|
||||
}
|
||||
}
|
||||
result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
|
||||
new Annotation[0];
|
||||
|
||||
/**
|
||||
* Parses the annotation at the current position in the specified
|
||||
* byte buffer, resolving constant references in the specified constant
|
||||
* pool. The cursor of the byte buffer must point to an "annotation
|
||||
* structure" as described in the RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* annotation {
|
||||
* u2 type_index;
|
||||
* u2 num_member_value_pairs;
|
||||
* { u2 member_name_index;
|
||||
* member_value value;
|
||||
* } member_value_pairs[num_member_value_pairs];
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Returns the annotation, or null if the annotation's type cannot
|
||||
* be found by the VM, or is not a valid annotation type.
|
||||
*
|
||||
* @param exceptionOnMissingAnnotationClass if true, throw
|
||||
* TypeNotPresentException if a referenced annotation type is not
|
||||
* available at runtime
|
||||
*/
|
||||
static Annotation parseAnnotation(ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container,
|
||||
boolean exceptionOnMissingAnnotationClass) {
|
||||
return parseAnnotation2(buf, constPool, container, exceptionOnMissingAnnotationClass, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Annotation parseAnnotation2(ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container,
|
||||
boolean exceptionOnMissingAnnotationClass,
|
||||
Class<? extends Annotation>[] selectAnnotationClasses) {
|
||||
int typeIndex = buf.getShort() & 0xFFFF;
|
||||
Class<? extends Annotation> annotationClass = null;
|
||||
String sig = "[unknown]";
|
||||
try {
|
||||
sig = constPool.getUTF8At(typeIndex);
|
||||
annotationClass = (Class<? extends Annotation>)parseSig(sig, container);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
if (exceptionOnMissingAnnotationClass)
|
||||
// note: at this point sig is "[unknown]" or VM-style
|
||||
// name instead of a binary name
|
||||
throw new TypeNotPresentException(sig, e);
|
||||
skipAnnotation(buf, false);
|
||||
return null;
|
||||
}
|
||||
catch (TypeNotPresentException e) {
|
||||
if (exceptionOnMissingAnnotationClass)
|
||||
throw e;
|
||||
skipAnnotation(buf, false);
|
||||
return null;
|
||||
}
|
||||
if (selectAnnotationClasses != null && !contains(selectAnnotationClasses, annotationClass)) {
|
||||
skipAnnotation(buf, false);
|
||||
return null;
|
||||
}
|
||||
AnnotationType type = null;
|
||||
try {
|
||||
type = AnnotationType.getInstance(annotationClass);
|
||||
} catch (IllegalArgumentException e) {
|
||||
skipAnnotation(buf, false);
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Class<?>> memberTypes = type.memberTypes();
|
||||
Map<String, Object> memberValues =
|
||||
new LinkedHashMap<String, Object>(type.memberDefaults());
|
||||
|
||||
int numMembers = buf.getShort() & 0xFFFF;
|
||||
for (int i = 0; i < numMembers; i++) {
|
||||
int memberNameIndex = buf.getShort() & 0xFFFF;
|
||||
String memberName = constPool.getUTF8At(memberNameIndex);
|
||||
Class<?> memberType = memberTypes.get(memberName);
|
||||
|
||||
if (memberType == null) {
|
||||
// Member is no longer present in annotation type; ignore it
|
||||
skipMemberValue(buf);
|
||||
} else {
|
||||
Object value = parseMemberValue(memberType, buf, constPool, container);
|
||||
if (value instanceof AnnotationTypeMismatchExceptionProxy)
|
||||
((AnnotationTypeMismatchExceptionProxy) value).setMember(type.members().get(memberName));
|
||||
memberValues.put(memberName, value);
|
||||
}
|
||||
}
|
||||
return annotationForMap(annotationClass, memberValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an annotation of the given type backed by the given
|
||||
* member {@literal ->} value map.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public static Annotation annotationForMap(final Class<? extends Annotation> type,
|
||||
final Map<String, Object> memberValues)
|
||||
{
|
||||
return AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
|
||||
public Annotation run() {
|
||||
return (Annotation) Proxy.newProxyInstance(
|
||||
type.getClassLoader(), new Class<?>[] { type },
|
||||
new AnnotationInvocationHandler(type, memberValues));
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the annotation member value at the current position in the
|
||||
* specified byte buffer, resolving constant references in the specified
|
||||
* constant pool. The cursor of the byte buffer must point to a
|
||||
* "member_value structure" as described in the
|
||||
* RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* member_value {
|
||||
* u1 tag;
|
||||
* union {
|
||||
* u2 const_value_index;
|
||||
* {
|
||||
* u2 type_name_index;
|
||||
* u2 const_name_index;
|
||||
* } enum_const_value;
|
||||
* u2 class_info_index;
|
||||
* annotation annotation_value;
|
||||
* {
|
||||
* u2 num_values;
|
||||
* member_value values[num_values];
|
||||
* } array_value;
|
||||
* } value;
|
||||
* }
|
||||
*
|
||||
* The member must be of the indicated type. If it is not, this
|
||||
* method returns an AnnotationTypeMismatchExceptionProxy.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Object parseMemberValue(Class<?> memberType,
|
||||
ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
// Note that VMSupport.encodeAnnotation (used by JVMCI) may need to
|
||||
// be updated if new annotation member types are added.
|
||||
Object result = null;
|
||||
int tag = buf.get();
|
||||
switch(tag) {
|
||||
case 'e':
|
||||
return parseEnumValue((Class<? extends Enum<?>>)memberType, buf, constPool, container);
|
||||
case 'c':
|
||||
result = parseClassValue(buf, constPool, container);
|
||||
break;
|
||||
case '@':
|
||||
result = parseAnnotation(buf, constPool, container, true);
|
||||
break;
|
||||
case '[':
|
||||
return parseArray(memberType, buf, constPool, container);
|
||||
default:
|
||||
result = parseConst(tag, buf, constPool);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = new AnnotationTypeMismatchExceptionProxy(
|
||||
Proxy.isProxyClass(memberType)
|
||||
? memberType.getInterfaces()[0].getName()
|
||||
: memberType.getName());
|
||||
} else if (!(result instanceof ExceptionProxy) &&
|
||||
!memberType.isInstance(result)) {
|
||||
if (result instanceof Annotation) {
|
||||
result = new AnnotationTypeMismatchExceptionProxy(
|
||||
result.toString());
|
||||
} else {
|
||||
result = new AnnotationTypeMismatchExceptionProxy(
|
||||
result.getClass().getName() + "[" + result + "]");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the primitive or String annotation member value indicated by
|
||||
* the specified tag byte at the current position in the specified byte
|
||||
* buffer, resolving constant reference in the specified constant pool.
|
||||
* The cursor of the byte buffer must point to an annotation member value
|
||||
* of the type indicated by the specified tag, as described in the
|
||||
* RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* u2 const_value_index;
|
||||
*/
|
||||
private static Object parseConst(int tag,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
int constIndex = buf.getShort() & 0xFFFF;
|
||||
switch(tag) {
|
||||
case 'B':
|
||||
return Byte.valueOf((byte) constPool.getIntAt(constIndex));
|
||||
case 'C':
|
||||
return Character.valueOf((char) constPool.getIntAt(constIndex));
|
||||
case 'D':
|
||||
return Double.valueOf(constPool.getDoubleAt(constIndex));
|
||||
case 'F':
|
||||
return Float.valueOf(constPool.getFloatAt(constIndex));
|
||||
case 'I':
|
||||
return Integer.valueOf(constPool.getIntAt(constIndex));
|
||||
case 'J':
|
||||
return Long.valueOf(constPool.getLongAt(constIndex));
|
||||
case 'S':
|
||||
return Short.valueOf((short) constPool.getIntAt(constIndex));
|
||||
case 'Z':
|
||||
return Boolean.valueOf(constPool.getIntAt(constIndex) != 0);
|
||||
case 's':
|
||||
return constPool.getUTF8At(constIndex);
|
||||
default:
|
||||
throw new AnnotationFormatError(
|
||||
"Invalid member-value tag in annotation: " + tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the Class member value at the current position in the
|
||||
* specified byte buffer, resolving constant references in the specified
|
||||
* constant pool. The cursor of the byte buffer must point to a "class
|
||||
* info index" as described in the RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* u2 class_info_index;
|
||||
*/
|
||||
private static Object parseClassValue(ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
int classIndex = buf.getShort() & 0xFFFF;
|
||||
try {
|
||||
String sig = constPool.getUTF8At(classIndex);
|
||||
return parseSig(sig, container);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
return new TypeNotPresentExceptionProxy("[unknown]", e);
|
||||
} catch (TypeNotPresentException e) {
|
||||
return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> parseSig(String sig, Class<?> container) {
|
||||
if (sig.equals("V")) return void.class;
|
||||
SignatureParser parser = SignatureParser.make();
|
||||
TypeSignature typeSig = parser.parseTypeSig(sig);
|
||||
GenericsFactory factory = CoreReflectionFactory.make(container, ClassScope.make(container));
|
||||
Reifier reify = Reifier.make(factory);
|
||||
typeSig.accept(reify);
|
||||
Type result = reify.getResult();
|
||||
return toClass(result);
|
||||
}
|
||||
static Class<?> toClass(Type o) {
|
||||
if (o instanceof GenericArrayType)
|
||||
return Array.newInstance(toClass(((GenericArrayType) o).getGenericComponentType()), 0)
|
||||
.getClass();
|
||||
return (Class)o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the enum constant member value at the current position in the
|
||||
* specified byte buffer, resolving constant references in the specified
|
||||
* constant pool. The cursor of the byte buffer must point to a
|
||||
* "enum_const_value structure" as described in the
|
||||
* RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* {
|
||||
* u2 type_name_index;
|
||||
* u2 const_name_index;
|
||||
* } enum_const_value;
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private static Object parseEnumValue(Class<? extends Enum> enumType, ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
int typeNameIndex = buf.getShort() & 0xFFFF;
|
||||
String typeName = constPool.getUTF8At(typeNameIndex);
|
||||
int constNameIndex = buf.getShort() & 0xFFFF;
|
||||
String constName = constPool.getUTF8At(constNameIndex);
|
||||
if (!enumType.isEnum() || enumType != parseSig(typeName, container)) {
|
||||
return new AnnotationTypeMismatchExceptionProxy(
|
||||
typeName.substring(1, typeName.length() - 1).replace('/', '.') + "." + constName);
|
||||
}
|
||||
|
||||
try {
|
||||
return Enum.valueOf(enumType, constName);
|
||||
} catch(IllegalArgumentException e) {
|
||||
return new EnumConstantNotPresentExceptionProxy(
|
||||
(Class<? extends Enum<?>>)enumType, constName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the array value at the current position in the specified byte
|
||||
* buffer, resolving constant references in the specified constant pool.
|
||||
* The cursor of the byte buffer must point to an array value struct
|
||||
* as specified in the RuntimeVisibleAnnotations_attribute:
|
||||
*
|
||||
* {
|
||||
* u2 num_values;
|
||||
* member_value values[num_values];
|
||||
* } array_value;
|
||||
*
|
||||
* If the array values do not match arrayType, an
|
||||
* AnnotationTypeMismatchExceptionProxy will be returned.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object parseArray(Class<?> arrayType,
|
||||
ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
int length = buf.getShort() & 0xFFFF; // Number of array components
|
||||
if (!arrayType.isArray()) {
|
||||
return parseUnknownArray(length, buf);
|
||||
}
|
||||
Class<?> componentType = arrayType.getComponentType();
|
||||
|
||||
if (componentType == byte.class) {
|
||||
return parseByteArray(length, buf, constPool);
|
||||
} else if (componentType == char.class) {
|
||||
return parseCharArray(length, buf, constPool);
|
||||
} else if (componentType == double.class) {
|
||||
return parseDoubleArray(length, buf, constPool);
|
||||
} else if (componentType == float.class) {
|
||||
return parseFloatArray(length, buf, constPool);
|
||||
} else if (componentType == int.class) {
|
||||
return parseIntArray(length, buf, constPool);
|
||||
} else if (componentType == long.class) {
|
||||
return parseLongArray(length, buf, constPool);
|
||||
} else if (componentType == short.class) {
|
||||
return parseShortArray(length, buf, constPool);
|
||||
} else if (componentType == boolean.class) {
|
||||
return parseBooleanArray(length, buf, constPool);
|
||||
} else if (componentType == String.class) {
|
||||
return parseStringArray(length, buf, constPool);
|
||||
} else if (componentType == Class.class) {
|
||||
return parseClassArray(length, buf, constPool, container);
|
||||
} else if (componentType.isEnum()) {
|
||||
return parseEnumArray(length, (Class<? extends Enum<?>>)componentType, buf,
|
||||
constPool, container);
|
||||
} else if (componentType.isAnnotation()) {
|
||||
return parseAnnotationArray(length, (Class <? extends Annotation>)componentType, buf,
|
||||
constPool, container);
|
||||
} else {
|
||||
return parseUnknownArray(length, buf);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object parseByteArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
byte[] result = new byte[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'B') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = (byte) constPool.getIntAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseCharArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
char[] result = new char[length];
|
||||
boolean typeMismatch = false;
|
||||
byte tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'C') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = (char) constPool.getIntAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseDoubleArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
double[] result = new double[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'D') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = constPool.getDoubleAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseFloatArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
float[] result = new float[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'F') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = constPool.getFloatAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseIntArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
int[] result = new int[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'I') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = constPool.getIntAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseLongArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
long[] result = new long[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'J') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = constPool.getLongAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseShortArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
short[] result = new short[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'S') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = (short) constPool.getIntAt(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseBooleanArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
boolean[] result = new boolean[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 'Z') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = (constPool.getIntAt(index) != 0);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseStringArray(int length,
|
||||
ByteBuffer buf, ConstantPool constPool) {
|
||||
String[] result = new String[length];
|
||||
boolean typeMismatch = false;
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
if (tag == 's') {
|
||||
int index = buf.getShort() & 0xFFFF;
|
||||
result[i] = constPool.getUTF8At(index);
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
typeMismatch = true;
|
||||
}
|
||||
}
|
||||
return typeMismatch ? exceptionProxy(tag) : result;
|
||||
}
|
||||
|
||||
private static Object parseClassArray(int length,
|
||||
ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
return parseArrayElements(new Class<?>[length],
|
||||
buf, 'c', () -> parseClassValue(buf, constPool, container));
|
||||
}
|
||||
|
||||
private static Object parseEnumArray(int length, Class<? extends Enum<?>> enumType,
|
||||
ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
return parseArrayElements((Object[]) Array.newInstance(enumType, length),
|
||||
buf, 'e', () -> parseEnumValue(enumType, buf, constPool, container));
|
||||
}
|
||||
|
||||
private static Object parseAnnotationArray(int length,
|
||||
Class<? extends Annotation> annotationType,
|
||||
ByteBuffer buf,
|
||||
ConstantPool constPool,
|
||||
Class<?> container) {
|
||||
return parseArrayElements((Object[]) Array.newInstance(annotationType, length),
|
||||
buf, '@', () -> parseAnnotation(buf, constPool, container, true));
|
||||
}
|
||||
|
||||
private static Object parseArrayElements(Object[] result,
|
||||
ByteBuffer buf,
|
||||
int expectedTag,
|
||||
Supplier<Object> parseElement) {
|
||||
Object exceptionProxy = null;
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
int tag = buf.get();
|
||||
if (tag == expectedTag) {
|
||||
Object value = parseElement.get();
|
||||
if (value instanceof ExceptionProxy) {
|
||||
if (exceptionProxy == null)
|
||||
exceptionProxy = ((ExceptionProxy) value);
|
||||
} else {
|
||||
result[i] = value;
|
||||
}
|
||||
} else {
|
||||
skipMemberValue(tag, buf);
|
||||
if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag);
|
||||
}
|
||||
}
|
||||
return (exceptionProxy != null) ? exceptionProxy : result;
|
||||
}
|
||||
|
||||
private static Object parseUnknownArray(int length,
|
||||
ByteBuffer buf) {
|
||||
int tag = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tag = buf.get();
|
||||
skipMemberValue(tag, buf);
|
||||
}
|
||||
|
||||
return exceptionProxy(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriate exception proxy for a mismatching array
|
||||
* annotation where the erroneous array has the specified tag.
|
||||
*/
|
||||
private static ExceptionProxy exceptionProxy(int tag) {
|
||||
return new AnnotationTypeMismatchExceptionProxy(
|
||||
"Array with component tag: " + (tag == 0 ? "0" : (char) tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the annotation at the current position in the specified
|
||||
* byte buffer. The cursor of the byte buffer must point to
|
||||
* an "annotation structure" OR two bytes into an annotation
|
||||
* structure (i.e., after the type index).
|
||||
*
|
||||
* @param complete true if the byte buffer points to the beginning
|
||||
* of an annotation structure (rather than two bytes in).
|
||||
*/
|
||||
private static void skipAnnotation(ByteBuffer buf, boolean complete) {
|
||||
if (complete)
|
||||
buf.getShort(); // Skip type index
|
||||
int numMembers = buf.getShort() & 0xFFFF;
|
||||
for (int i = 0; i < numMembers; i++) {
|
||||
buf.getShort(); // Skip memberNameIndex
|
||||
skipMemberValue(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the annotation member value at the current position in the
|
||||
* specified byte buffer. The cursor of the byte buffer must point to a
|
||||
* "member_value structure."
|
||||
*/
|
||||
private static void skipMemberValue(ByteBuffer buf) {
|
||||
int tag = buf.get();
|
||||
skipMemberValue(tag, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the annotation member value at the current position in the
|
||||
* specified byte buffer. The cursor of the byte buffer must point
|
||||
* immediately after the tag in a "member_value structure."
|
||||
*/
|
||||
private static void skipMemberValue(int tag, ByteBuffer buf) {
|
||||
switch(tag) {
|
||||
case 'e': // Enum value
|
||||
buf.getInt(); // (Two shorts, actually.)
|
||||
break;
|
||||
case '@':
|
||||
skipAnnotation(buf, true);
|
||||
break;
|
||||
case '[':
|
||||
skipArray(buf);
|
||||
break;
|
||||
default:
|
||||
// Class, primitive, or String
|
||||
buf.getShort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the array value at the current position in the specified byte
|
||||
* buffer. The cursor of the byte buffer must point to an array value
|
||||
* struct.
|
||||
*/
|
||||
private static void skipArray(ByteBuffer buf) {
|
||||
int length = buf.getShort() & 0xFFFF;
|
||||
for (int i = 0; i < length; i++)
|
||||
skipMemberValue(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for given {@code element} in given {@code array} by identity.
|
||||
* Returns {@code true} if found {@code false} if not.
|
||||
*/
|
||||
private static boolean contains(Object[] array, Object element) {
|
||||
for (Object e : array)
|
||||
if (e == element)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method converts the annotation map returned by the parseAnnotations()
|
||||
* method to an array. It is called by Field.getDeclaredAnnotations(),
|
||||
* Method.getDeclaredAnnotations(), and Constructor.getDeclaredAnnotations().
|
||||
* This avoids the reflection classes to load the Annotation class until
|
||||
* it is needed.
|
||||
*/
|
||||
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
|
||||
public static Annotation[] toArray(Map<Class<? extends Annotation>, Annotation> annotations) {
|
||||
return annotations.values().toArray(EMPTY_ANNOTATION_ARRAY);
|
||||
}
|
||||
|
||||
static Annotation[] getEmptyAnnotationArray() { return EMPTY_ANNOTATION_ARRAY; }
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.sun.reflect.annotation;
|
||||
|
||||
import sun.reflect.annotation.AnnotationType;
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class AnnotationTypeAccess {
|
||||
private AnnotationTypeAccess() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static Class<?> invocationHandlerReturnType(Class<?> cls) {
|
||||
return AnnotationType.invocationHandlerReturnType(cls);
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.sun.reflect.annotation;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.Method;
|
||||
import sun.reflect.annotation.ExceptionProxy;
|
||||
|
||||
/**
|
||||
* ExceptionProxy for AnnotationTypeMismatchException.
|
||||
*
|
||||
* @author Josh Bloch
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("Since15")
|
||||
class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 7844069490309503934L;
|
||||
@SuppressWarnings("serial") // Not statically typed as Serializable
|
||||
private Method member; // Would be more robust to null-out in a writeObject method.
|
||||
private final String foundType;
|
||||
|
||||
/**
|
||||
* It turns out to be convenient to construct these proxies in
|
||||
* two stages. Since this is a private implementation class, we
|
||||
* permit ourselves this liberty even though it's normally a very
|
||||
* bad idea.
|
||||
*/
|
||||
AnnotationTypeMismatchExceptionProxy(String foundType) {
|
||||
this.foundType = foundType;
|
||||
}
|
||||
|
||||
AnnotationTypeMismatchExceptionProxy setMember(Method member) {
|
||||
this.member = member;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected RuntimeException generateException() {
|
||||
return new AnnotationTypeMismatchException(member, foundType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "/* Warning type mismatch! \"" + foundType + "\" */" ;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.sun.reflect.annotation;
|
||||
|
||||
import sun.reflect.annotation.ExceptionProxy;
|
||||
|
||||
/**
|
||||
* ExceptionProxy for EnumConstantNotPresentException.
|
||||
*
|
||||
* @author Josh Bloch
|
||||
* @since 1.5
|
||||
*/
|
||||
public class EnumConstantNotPresentExceptionProxy extends ExceptionProxy {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -604662101303187330L;
|
||||
final Class<? extends Enum<?>> enumType;
|
||||
final String constName;
|
||||
|
||||
public EnumConstantNotPresentExceptionProxy(Class<? extends Enum<?>> enumType,
|
||||
String constName) {
|
||||
this.enumType = enumType;
|
||||
this.constName = constName;
|
||||
}
|
||||
|
||||
protected RuntimeException generateException() {
|
||||
return new EnumConstantNotPresentException(enumType, constName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return constName + " /* Warning: constant not present! */";
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.sun.reflect.annotation;
|
||||
|
||||
/**
|
||||
* An instance of this class is stored in an AnnotationInvocationHandler's
|
||||
* "memberValues" map in lieu of a value for an annotation member that
|
||||
* cannot be returned due to some exceptional condition (typically some
|
||||
* form of illegal evolution of the annotation class). The ExceptionProxy
|
||||
* instance describes the exception that the dynamic proxy should throw if
|
||||
* it is queried for this member.
|
||||
*
|
||||
* @author Josh Bloch
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("Since15")
|
||||
public abstract class ExceptionProxy implements java.io.Serializable {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 7241930048386631401L;
|
||||
protected abstract RuntimeException generateException();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.itsaky.androidide.plugins.JavacJackpotPlugin
|
||||
|
||||
@Suppress("JavaPluginLanguageLevel")
|
||||
plugins {
|
||||
id("java-library")
|
||||
}
|
||||
|
||||
apply {
|
||||
plugin(JavacJackpotPlugin::class.java)
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.subprojects.javacDeps)
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class JmodFileWrapper {
|
||||
|
||||
public static void checkMagic(Path file) throws IOException {
|
||||
//TODO: perform validation
|
||||
}
|
||||
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModuleWrapper {
|
||||
private final Class<?> clazz;
|
||||
|
||||
private ModuleWrapper(Class<?> c) {
|
||||
this.clazz = c;
|
||||
}
|
||||
|
||||
public static ModuleWrapper getModule(Class<?> clazz) {
|
||||
return new ModuleWrapper(clazz);
|
||||
}
|
||||
|
||||
public static ModuleWrapper getUnnamedModule(ClassLoader loader) {
|
||||
return new ModuleWrapper(null);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
switch (clazz.getName()) {
|
||||
case "jdk.javadoc.internal.api.JavadocTool": return "jdk.javadoc";
|
||||
case "com.sun.tools.javac.api.JavacTool": return "jdk.compiler";
|
||||
default: return "jdk.compiler"; //XXX
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNamed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addExports(String pack, ModuleWrapper to) {
|
||||
}
|
||||
|
||||
public <S> void addUses(Class<S> service) {
|
||||
if (this.clazz != null) {
|
||||
ensureUses(this.clazz, service);
|
||||
}
|
||||
ensureUses(service);
|
||||
}
|
||||
|
||||
static void ensureUses(Class<?> clazz) {
|
||||
// ServiceLoaderWrapper.class.getModule().addUses(aClass);
|
||||
Class<?> thisClass = ServiceLoaderWrapper.class;
|
||||
ensureUses(thisClass, clazz);
|
||||
}
|
||||
|
||||
private static void ensureUses(Class<?> thisClass, Class<?> clazz) {
|
||||
try {
|
||||
final Class<?> Module = Class.forName("java.lang.Module");
|
||||
final Method addUses = Module.getDeclaredMethod("addUses", Class.class);
|
||||
final Method getModule = Class.class.getDeclaredMethod("getModule");
|
||||
final Object thisClassModule = getModule.invoke(thisClass);
|
||||
addUses.invoke(thisClassModule, clazz);
|
||||
} catch (SecurityException | ReflectiveOperationException t) {
|
||||
//ignore - might log?
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModuleFinder {
|
||||
|
||||
public static ModuleFinder of(Path... paths) {
|
||||
return new ModuleFinder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ModuleLayer {
|
||||
|
||||
public static ModuleLayer boot() {
|
||||
return new ModuleLayer();
|
||||
}
|
||||
|
||||
public Configuration configuration() {
|
||||
return new Configuration();
|
||||
}
|
||||
|
||||
public ModuleLayer defineModulesWithOneLoader(Configuration cf, ClassLoader systemClassLoader) {
|
||||
return new ModuleLayer();
|
||||
}
|
||||
|
||||
public Set<ModuleWrapper> modules() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Configuration {
|
||||
|
||||
public Configuration resolveAndBind(ModuleFinder of, ModuleFinder finder, Set<?> emptySet) {
|
||||
return new Configuration();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ModuleDescriptor {
|
||||
public static class Version {
|
||||
public static void parse(String v) {
|
||||
//TODO: do validation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class ObjectsWrapper {
|
||||
|
||||
public static <T> T requireNonNullElse(T value, T elseValue) {
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
requireNonNull(elseValue, "elseValue");
|
||||
return elseValue;
|
||||
}
|
||||
|
||||
public static int checkFromIndexSize(int fromIndex, int size, int length) {
|
||||
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
|
||||
throw new IndexOutOfBoundsException("" + fromIndex + ", " + size + ", " + length);
|
||||
return fromIndex;
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class PathWrapper {
|
||||
public static Path of(URI uri) {
|
||||
//TODO: proper implementation:
|
||||
return new File(uri).toPath();
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.lang.IllegalStateException;
|
||||
import java.lang.String;
|
||||
|
||||
public final class RuntimeWR {
|
||||
private RuntimeWR() {
|
||||
}
|
||||
|
||||
public static final class Version {
|
||||
private Version() {
|
||||
}
|
||||
|
||||
public int feature() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
public static RuntimeWR.Version parse(String v) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
public class ServiceLoaderWrapper<T> implements Iterable<T> {
|
||||
private final ServiceLoader<T> loader;
|
||||
|
||||
public ServiceLoaderWrapper(ServiceLoader<T> loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public static <T> ServiceLoader<T> load(ModuleWrapper.ModuleLayer layer, Class<T> aClass) {
|
||||
ModuleWrapper.ensureUses(aClass);
|
||||
return ServiceLoader.load(aClass); //XXX
|
||||
}
|
||||
|
||||
public static <T> ServiceLoaderWrapper<T> load(Class<T> aClass) {
|
||||
ModuleWrapper.ensureUses(aClass);
|
||||
return new ServiceLoaderWrapper<T>(ServiceLoader.load(aClass));
|
||||
}
|
||||
|
||||
public static <T> ServiceLoaderWrapper<T> load(Class<T> aClass, ClassLoader classLoader) {
|
||||
ModuleWrapper.ensureUses(aClass);
|
||||
return new ServiceLoaderWrapper<T>(ServiceLoader.load(aClass, classLoader));
|
||||
}
|
||||
|
||||
public static <T> ServiceLoader<T> loadWithClassLoader(Class<T> aClass, ClassLoader classLoader) {
|
||||
ModuleWrapper.ensureUses(aClass);
|
||||
return ServiceLoader.load(aClass, classLoader);
|
||||
}
|
||||
|
||||
public static <T> ServiceLoader<T> loadTool(Class<T> toolClass) {
|
||||
ModuleWrapper.ensureUses(toolClass);
|
||||
ServiceLoader<T> res = ServiceLoader.load(toolClass, ToolProvider.class.getClassLoader());
|
||||
if (res.iterator().hasNext()) {
|
||||
return res;
|
||||
}
|
||||
return ServiceLoader.load(toolClass, ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public Stream<Provider<T>> stream() {
|
||||
return StreamSupport.stream(loader.spliterator(), false)
|
||||
.map(v -> new Provider<T>() {
|
||||
@Override
|
||||
public Class<? extends T> type() {
|
||||
return (Class<T>) v.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return v;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return loader.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super T> action) {
|
||||
loader.forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<T> spliterator() {
|
||||
return loader.spliterator();
|
||||
}
|
||||
|
||||
public interface Provider<T> {
|
||||
public Class<? extends T> type();
|
||||
public T get();
|
||||
}
|
||||
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StringWrapper {
|
||||
|
||||
public static String stripIndent(String str) {
|
||||
//prototype only, needs testing and performance improvements:
|
||||
String[] lines = lines(str);
|
||||
int indent = Arrays.stream(lines)
|
||||
.mapToInt((s) -> {
|
||||
int at = 0;
|
||||
while (at < s.length() && Character.isWhitespace(s.charAt(at))) {
|
||||
at++;
|
||||
}
|
||||
if (at == s.length()) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return at;
|
||||
}).min()
|
||||
.orElse(Integer.MAX_VALUE);
|
||||
if (lines.length > 0 && isBlank(lines[lines.length - 1])) {
|
||||
indent = Math.min(indent, lines[lines.length - 1].length());
|
||||
}
|
||||
int indentFin = indent;
|
||||
return Arrays.stream(lines).map(line -> {
|
||||
if (line.length() <= indentFin) {
|
||||
return "";
|
||||
}
|
||||
String l = line.substring(indentFin);
|
||||
int at = l.length() - 1;
|
||||
while (at >= 0 && Character.isWhitespace(l.charAt(at))) {
|
||||
at--;
|
||||
}
|
||||
return l.substring(0, at + 1);
|
||||
}).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static String stripTrailing(String str) {
|
||||
int end = str.length();
|
||||
while (end > 0 && Character.isWhitespace(str.charAt(end - 1)))
|
||||
end--;
|
||||
return str.substring(0, end);
|
||||
}
|
||||
|
||||
public static boolean isBlank(String s) {
|
||||
int at = 0;
|
||||
while (at < s.length() && Character.isWhitespace(s.charAt(at))) {
|
||||
at++;
|
||||
}
|
||||
return at == s.length();
|
||||
}
|
||||
|
||||
//copied from nb-javac:
|
||||
public static String translateEscapes(String str) {
|
||||
if (str.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
char[] chars = str.toCharArray();
|
||||
int length = chars.length;
|
||||
int from = 0;
|
||||
int to = 0;
|
||||
while (from < length) {
|
||||
char ch = chars[from++];
|
||||
if (ch == '\\') {
|
||||
ch = from < length ? chars[from++] : '\0';
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
ch = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
ch = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
ch = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
ch = '\r';
|
||||
break;
|
||||
case 't':
|
||||
ch = '\t';
|
||||
break;
|
||||
case 's':
|
||||
ch = ' ';
|
||||
break;
|
||||
case '\'':
|
||||
case '\"':
|
||||
case '\\':
|
||||
// as is
|
||||
break;
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
int limit = Integer.min(from + (ch <= '3' ? 2 : 1), length);
|
||||
int code = ch - '0';
|
||||
while (from < limit) {
|
||||
ch = chars[from];
|
||||
if (ch < '0' || '7' < ch) {
|
||||
break;
|
||||
}
|
||||
from++;
|
||||
code = (code << 3) | (ch - '0');
|
||||
}
|
||||
ch = (char)code;
|
||||
break;
|
||||
case '\n': continue;
|
||||
default: {
|
||||
String msg = String.format(
|
||||
"Invalid escape sequence: \\%c \\\\u%04X",
|
||||
ch, (int)ch);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chars[to++] = ch;
|
||||
}
|
||||
|
||||
return new String(chars, 0, to);
|
||||
}
|
||||
|
||||
private static String[] lines(String txt) {
|
||||
List<String> arr = new ArrayList<>();
|
||||
int at = 0;
|
||||
for (;;) {
|
||||
int next = txt.indexOf('\n', at);
|
||||
if (next == -1) {
|
||||
arr.add(txt.substring(at));
|
||||
break;
|
||||
}
|
||||
arr.add(txt.substring(at, next));
|
||||
at = next + 1;
|
||||
}
|
||||
return arr.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.itsaky.androidide.javac.wrappers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.WatchService;
|
||||
import java.nio.file.attribute.UserPrincipalLookupService;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class VMWrapper {
|
||||
private VMWrapper() {
|
||||
}
|
||||
|
||||
public static String[] getRuntimeArguments() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
private static final String[] symbolFileLocation = { "lib", "ct.sym" };
|
||||
private static Reference<Path> cachedCtSym = new SoftReference<>(null);
|
||||
|
||||
public static Path findCtSym() {
|
||||
Path obj = cachedCtSym.get();
|
||||
if (obj instanceof Path) {
|
||||
return obj;
|
||||
}
|
||||
try {
|
||||
ClassLoader loader = VMWrapper.class.getClassLoader();
|
||||
if (loader == null) {
|
||||
loader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
Enumeration<URL> en = loader.getResources("META-INF/services/com.sun.tools.javac.platform.PlatformProvider");
|
||||
URL res = en.hasMoreElements() ? en.nextElement() : null;
|
||||
if (res == null) {
|
||||
//runnning inside a JDK image, try to look for lib/ct.sym:
|
||||
String javaHome = System.getProperty("java.home");
|
||||
Path file = Paths.get(javaHome);
|
||||
for (String name : symbolFileLocation) {
|
||||
file = file.resolve(name);
|
||||
}
|
||||
if (!Files.exists(file)) {
|
||||
throw new IllegalStateException("Cannot find ct.sym at " + file);
|
||||
}
|
||||
return FileSystems.newFileSystem(file, (ClassLoader)null).getRootDirectories().iterator().next();
|
||||
}
|
||||
if (!res.getProtocol().equals("jar")) {
|
||||
res = en.hasMoreElements() ? en.nextElement() : null;
|
||||
}
|
||||
URL jar = ((JarURLConnection)res.openConnection()).getJarFileURL();
|
||||
Path path = Paths.get(jar.toURI());
|
||||
FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null);
|
||||
Path ctSym = fs.getPath("META-INF", "ct.sym");
|
||||
cachedCtSym = new SoftReference<>(ctSym);
|
||||
return ctSym;
|
||||
} catch (IOException | URISyntaxException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException {
|
||||
List<Path> all = new ArrayList<>();
|
||||
for (Path ch : Files.newDirectoryStream(dir)) {
|
||||
final String fileName = ch.getFileName().toString();
|
||||
if (fileName.endsWith("/")) {
|
||||
all.add(dir.resolve(fileName.substring(0, fileName.length() - 1)));
|
||||
} else {
|
||||
all.add(ch);
|
||||
}
|
||||
}
|
||||
return new DirectoryStream<Path>() {
|
||||
@Override
|
||||
public Iterator<Path> iterator() {
|
||||
return all.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static FileSystem pathFs(Path p) {
|
||||
return new FileSystem() {
|
||||
@Override
|
||||
public FileSystemProvider provider() {
|
||||
return p.getFileSystem().provider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return p.getFileSystem().isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return p.getFileSystem().isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSeparator() {
|
||||
return p.getFileSystem().getSeparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Path> getRootDirectories() {
|
||||
return Collections.singleton(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<FileStore> getFileStores() {
|
||||
return p.getFileSystem().getFileStores();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> supportedFileAttributeViews() {
|
||||
return p.getFileSystem().supportedFileAttributeViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath(String first, String... more) {
|
||||
Path r = p.resolve(first);
|
||||
for (String m : more) {
|
||||
r = r.resolve(m);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher getPathMatcher(String syntaxAndPattern) {
|
||||
return p.getFileSystem().getPathMatcher(syntaxAndPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipalLookupService getUserPrincipalLookupService() {
|
||||
return p.getFileSystem().getUserPrincipalLookupService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchService newWatchService() throws IOException {
|
||||
return p.getFileSystem().newWatchService();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.itsaky.androidide.com.sun.tools.javac.platform.JDKPlatformProvider
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.itsaky.androidide.com.sun.tools.javac.api.JavacTool
|
||||
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
java.util.Set.of($args$) => java.util.Collections.unmodifiableSet(new java.util.HashSet<>(java.util.Arrays.asList($args$)));;
|
||||
java.util.List.of($args$) => java.util.Collections.unmodifiableList(java.util.Arrays.asList($args$));;
|
||||
java.util.List.copyOf($list) => java.util.Collections.unmodifiableList(new java.util.ArrayList<>($list));;
|
||||
java.util.Map.of($k, $v) => java.util.Collections.unmodifiableMap(java.util.Collections.singletonMap($k, $v));;
|
||||
|
||||
//XXX: incorrect!
|
||||
java.lang.Runtime.version().feature() =>
|
||||
"1.8".equals(System.getProperty("java.specification.version")) ? 8 : Integer.parseInt(System.getProperty("java.specification.version"))
|
||||
;;
|
||||
|
||||
|
||||
@java.io.Serial => ;;
|
||||
@java.lang.Deprecated($arg1, $args$) => @java.lang.Deprecated;;
|
||||
|
||||
|
||||
$1.flip() :: $1 instanceof java.nio.ByteBuffer
|
||||
=>
|
||||
((ByteBuffer) $1.flip())
|
||||
;;
|
||||
|
||||
$1.clear() :: $1 instanceof java.nio.ByteBuffer
|
||||
=>
|
||||
((ByteBuffer) $1.clear())
|
||||
;;
|
||||
|
||||
$1.flip() :: $1 instanceof java.nio.CharBuffer && !parentMatches("$1.flip();")
|
||||
=>
|
||||
((CharBuffer) $1.flip())
|
||||
;;
|
||||
|
||||
java.nio.file.Path.of($uri) :: $uri instanceof java.net.URI
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.PathWrapper.of($uri)
|
||||
;;
|
||||
|
||||
java.lang.module.ModuleDescriptor
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ModuleWrapper.ModuleDescriptor
|
||||
;;
|
||||
|
||||
$class.getModule() :: $class instanceof java.lang.Class
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ModuleWrapper.getModule($class)
|
||||
;;
|
||||
|
||||
$cl.getUnnamedModule() :: $cl instanceof java.lang.ClassLoader
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ModuleWrapper.getUnnamedModule($cl)
|
||||
;;
|
||||
|
||||
java.lang.Runtime.Version => com.itsaky.androidide.javac.wrappers.RuntimeWR.Version;;
|
||||
|
||||
java.lang.Module => com.itsaky.androidide.javac.wrappers.ModuleWrapper;;
|
||||
|
||||
java.lang.module.ModuleFinder => com.itsaky.androidide.javac.wrappers.ModuleWrapper.ModuleFinder;;
|
||||
java.lang.ModuleLayer => com.itsaky.androidide.javac.wrappers.ModuleWrapper.ModuleLayer;;
|
||||
java.lang.module.Configuration => com.itsaky.androidide.javac.wrappers.ModuleWrapper.Configuration;;
|
||||
|
||||
java.util.ServiceLoader.load($layer, $class) :: $layer instanceof java.lang.ModuleLayer && $class instanceof java.lang.Class
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ServiceLoaderWrapper.load($layer, $class)
|
||||
;;
|
||||
|
||||
java.util.ServiceLoader.load($class, $loader) :: $loader instanceof java.lang.ClassLoader && $class instanceof java.lang.Class && !inClass("com.itsaky.androidide.com.sun.tools.doclint.DocLint") && !inClass("com.itsaky.androidide.javax.tools.ToolProvider");
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ServiceLoaderWrapper.loadWithClassLoader($class, $loader)
|
||||
;;
|
||||
|
||||
java.util.ServiceLoader.load($class, $loader) :: $loader instanceof java.lang.ClassLoader && $class instanceof java.lang.Class && inClass("com.itsaky.androidide.javax.tools.ToolProvider");
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ServiceLoaderWrapper.loadTool($class)
|
||||
;;
|
||||
|
||||
jdk.internal.jmod.JmodFile => com.itsaky.androidide.javac.wrappers.JmodFileWrapper;;
|
||||
|
||||
$1.isEmpty() :: $1 instanceof java.util.Optional
|
||||
=>
|
||||
!$1.isPresent()
|
||||
;;
|
||||
|
||||
java.util.Objects.requireNonNullElse($obj, $def)
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ObjectsWrapper.requireNonNullElse($obj, $def)
|
||||
;;
|
||||
|
||||
java.util.Objects.checkFromIndexSize($from, $size, $len)
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ObjectsWrapper.checkFromIndexSize($from, $size, $len)
|
||||
;;
|
||||
|
||||
java.util.ServiceLoader :: inClass("com.itsaky.androidide.com.sun.tools.doclint.DocLint")
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.ServiceLoaderWrapper
|
||||
;;
|
||||
|
||||
$1.stripTrailing() :: $1 instanceof java.lang.String
|
||||
=>
|
||||
$1.replaceAll("\\p{javaWhitespace}+$", "")
|
||||
;;
|
||||
|
||||
$1.stripLeading() :: $1 instanceof java.lang.String
|
||||
=>
|
||||
$1.replaceAll("^\\p{javaWhitespace}+", "")
|
||||
;;
|
||||
|
||||
$1.strip() :: $1 instanceof java.lang.String
|
||||
=>
|
||||
$1.replaceAll("^\\p{javaWhitespace}+", "").replaceAll("\\p{javaWhitespace}+$", "")
|
||||
;;
|
||||
|
||||
$1.stripIndent() :: $1 instanceof java.lang.String
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.StringWrapper.stripIndent($1)
|
||||
;;
|
||||
|
||||
$1.stripTrailing() //:: $1 instanceof java.lang.String
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.StringWrapper.stripTrailing($1)
|
||||
;;
|
||||
|
||||
$1.translateEscapes() :: $1 instanceof java.lang.String
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.StringWrapper.translateEscapes($1)
|
||||
;;
|
||||
|
||||
$1.isBlank() :: $1 instanceof java.lang.String
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.StringWrapper.isBlank($1)
|
||||
;;
|
||||
|
||||
$1.formatted($args$) :: $1 instanceof java.lang.String
|
||||
=>
|
||||
String.format($1, $args$)
|
||||
;;
|
||||
|
||||
jdk.internal.misc.VM => com.itsaky.androidide.javac.wrappers.VMWrapper;;
|
||||
|
||||
java.lang.ClassLoader.getSystemClassLoader() :: !inClass("com.itsaky.androidide.javax.tools.ToolProvider")
|
||||
=> com.itsaky.androidide.javax.tools.ToolProvider.class.getClassLoader();;
|
||||
|
||||
//TODO: workaround for the NetBeans' approach to javac loading using two different ClassLoaders:
|
||||
public static com.itsaky.androidide.com.sun.source.util.JavacTask instance(com.itsaky.androidide.javax.annotation.processing.ProcessingEnvironment processingEnvironment) {$whatever$;}
|
||||
=>
|
||||
public static com.itsaky.androidide.com.sun.source.util.JavacTask instance(com.itsaky.androidide.javax.annotation.processing.ProcessingEnvironment processingEnvironment) {
|
||||
if (!processingEnvironment.getClass().getName().equals(
|
||||
"com.itsaky.androidide.com.sun.tools.javac.processing.JavacProcessingEnvironment"))
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
try {
|
||||
//Cannot refer to the implementation directly, needs to use reflection:
|
||||
java.lang.reflect.Method m = processingEnvironment.getClass().getMethod("getJavacTask");
|
||||
|
||||
return (JavacTask) m.invoke(processingEnvironment);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new UnsupportedOperationException(ex);
|
||||
} catch (SecurityException ex) {
|
||||
throw new UnsupportedOperationException(ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new UnsupportedOperationException(ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new UnsupportedOperationException(ex);
|
||||
} catch (java.lang.reflect.InvocationTargetException ex) {
|
||||
throw new UnsupportedOperationException(ex);
|
||||
}
|
||||
}
|
||||
;;
|
||||
|
||||
$1.resolve("jrt-fs.jar").toUri().toURL() :: $1 instanceof java.nio.file.Path
|
||||
=>
|
||||
$1.resolve("lib").resolve("jrt-fs.jar").toUri().toURL()
|
||||
;;
|
||||
|
||||
|
||||
com.itsaky.androidide.javax.tools.ToolProvider.getSystemTool($class, $module, $name)
|
||||
=>
|
||||
$class.cast(((java.util.function.Supplier<Object>) () -> {
|
||||
try {
|
||||
Object v;
|
||||
try {
|
||||
v = com.itsaky.androidide.javax.tools.ToolProvider.getSystemTool($class, $module, $name);
|
||||
} catch (Error err) {
|
||||
v = null;
|
||||
}
|
||||
if (v == null) {
|
||||
v = Class.forName($name).newInstance();
|
||||
}
|
||||
return $class.cast(v);
|
||||
} catch (Throwable t) {
|
||||
throw new Error(t);
|
||||
}
|
||||
}).get())
|
||||
;;
|
||||
|
||||
static java.nio.file.Path findCtSym() {
|
||||
$code$
|
||||
}
|
||||
=>
|
||||
static java.nio.file.Path findCtSym() {
|
||||
return com.itsaky.androidide.javac.wrappers.VMWrapper.findCtSym()
|
||||
}
|
||||
;;
|
||||
|
||||
java.nio.file.FileSystems.newFileSystem($path, $a2)
|
||||
:: inClass("com.itsaky.androidide.com.sun.tools.javac.platform.JDKPlatformProvider")
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.VMWrapper.pathFs($path)
|
||||
;;
|
||||
|
||||
/*
|
||||
String $v = Character.toString($ch); :: $ch instanceof char
|
||||
=>
|
||||
if (!Character.isJavaIdentifierPart($ch)) {
|
||||
continue;
|
||||
}
|
||||
String $v = "" + $ch;
|
||||
;;
|
||||
*/
|
||||
|
||||
java.nio.file.Files.newDirectoryStream($a1)
|
||||
:: inClass("com.itsaky.androidide.com.sun.tools.javac.platform.JDKPlatformProvider")
|
||||
=>
|
||||
com.itsaky.androidide.javac.wrappers.VMWrapper.newDirectoryStream($a1)
|
||||
;;
|
||||
|
||||
$1.toList() :: $1 instanceof java.util.stream.Stream
|
||||
=>
|
||||
$1.collect(java.util.stream.Collectors.toList())
|
||||
;;
|
||||
|
||||
//to eliminate imports:
|
||||
com.itsaky.androidide.jdk.internal.javac.PreviewFeature
|
||||
=>
|
||||
java.lang.VirtualMachineError
|
||||
;;
|
||||
|
||||
//to eliminate imports:
|
||||
jdk.internal.javac.NoPreview
|
||||
=>
|
||||
java.lang.VirtualMachineError
|
||||
;;
|
||||
|
||||
//to eliminate imports:
|
||||
java.io.Serial
|
||||
=>
|
||||
java.lang.VirtualMachineError
|
||||
;;
|
||||
|
||||
// fallback to highest available ct.sym
|
||||
com.itsaky.androidide.com.sun.tools.javac.platform.PlatformUtils.lookupPlatformDescription($s)
|
||||
=>
|
||||
((java.util.function.Supplier<com.itsaky.androidide.com.sun.tools.javac.platform.PlatformDescription>) () -> {
|
||||
final String version = $s;
|
||||
com.itsaky.androidide.com.sun.tools.javac.platform.PlatformDescription pd = com.itsaky.androidide.com.sun.tools.javac.platform.PlatformUtils.lookupPlatformDescription(version);
|
||||
if (pd != null) {
|
||||
return pd;
|
||||
}
|
||||
|
||||
com.itsaky.androidide.com.sun.tools.javac.platform.PlatformDescription fallback = null;
|
||||
for (int numVersion = Integer.valueOf(version); fallback == null; numVersion--) {
|
||||
fallback = PlatformUtils.lookupPlatformDescription("" + numVersion);
|
||||
}
|
||||
if (fallback == null) {
|
||||
throw new NullPointerException("No platform found for " + version);
|
||||
}
|
||||
final com.itsaky.androidide.com.sun.tools.javac.platform.PlatformDescription delegate = fallback;
|
||||
|
||||
return new com.itsaky.androidide.com.sun.tools.javac.platform.PlatformDescription() {
|
||||
@Override
|
||||
public com.itsaky.androidide.javax.tools.JavaFileManager getFileManager() {
|
||||
return delegate.getFileManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.List<PlatformDescription.PluginInfo<com.itsaky.androidide.javax.annotation.processing.Processor>> getAnnotationProcessors() {
|
||||
return delegate.getAnnotationProcessors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.List<com.itsaky.androidide.com.sun.tools.javac.platform.PlatformDescription.PluginInfo<com.itsaky.androidide.com.sun.source.util.Plugin>> getPlugins() {
|
||||
return delegate.getPlugins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.List<String> getAdditionalOptions() {
|
||||
return delegate.getAdditionalOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
delegate.close();
|
||||
}
|
||||
};
|
||||
}).get();
|
||||
;;
|
||||
|
||||
$cs.isEmpty() :: $cs instanceof java.lang.CharSequence
|
||||
=>
|
||||
($cs.length() == 0)
|
||||
;;
|
||||
|
||||
$coll.toArray($type[]::new) :: inClass("com.itsaky.androidide.com.sun.tools.javac.comp.Flow")
|
||||
=>
|
||||
$coll.toArray(new $type[0])
|
||||
;;
|
||||
|
||||
$coll.toArray($expr) :: inClass("com.itsaky.androidide.com.sun.tools.javac.comp.Flow") && containsAny($expr, "new com.itsaky.androidide.com.sun.tools.javac.code.Type[$s]")
|
||||
=>
|
||||
$coll.toArray(new com.itsaky.androidide.com.sun.tools.javac.code.Type[0])
|
||||
;;
|
||||
|
||||
|
||||
Iterable<String> $it;
|
||||
try {
|
||||
$any$;
|
||||
} catch $catches$ :: inClass("com.itsaky.androidide.com.sun.tools.javac.main.Main")
|
||||
=>
|
||||
Iterable<String> $it = java.util.Arrays.asList(argv);
|
||||
;;
|
||||
|
||||
jdk.internal.opt.CommandLine
|
||||
=>
|
||||
java.lang.VirtualMachineError
|
||||
;;
|
||||
|
||||
jdk.internal.opt.CommandLine.UnmatchedQuote
|
||||
=>
|
||||
java.lang.VirtualMachineError
|
||||
;;
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
sun.reflect.annotation.ExceptionProxy
|
||||
=>
|
||||
com.itsaky.androidide.sun.reflect.annotation.ExceptionProxy
|
||||
;;
|
||||
|
||||
sun.reflect.annotation.AnnotationParser
|
||||
=>
|
||||
com.itsaky.androidide.sun.reflect.annotation.AnnotationParser
|
||||
;;
|
||||
|
||||
sun.reflect.annotation.EnumConstantNotPresentExceptionProxy
|
||||
=>
|
||||
com.itsaky.androidide.sun.reflect.annotation.EnumConstantNotPresentExceptionProxy
|
||||
;;
|
||||
|
||||
sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy
|
||||
=>
|
||||
com.itsaky.androidide.sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy
|
||||
;;
|
||||
|
||||
sun.reflect.annotation.AnnotationType.invocationHandlerReturnType($returnClass)
|
||||
=>
|
||||
com.itsaky.androidide.sun.reflect.annotation.AnnotationTypeAccess.invocationHandlerReturnType($returnClass)
|
||||
;;
|
||||
Reference in New Issue
Block a user