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.common.antlr4)
|
||||||
implementation(libs.google.gson)
|
implementation(libs.google.gson)
|
||||||
implementation(libs.google.java.format)
|
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) }
|
sourceSets.getByName("main").java { srcDir(target.propsDir) }
|
||||||
}
|
}
|
||||||
|
|
||||||
target.tasks.register("compileProperties") {
|
// target.tasks.register("compileProperties") {
|
||||||
doLast {
|
// doLast {
|
||||||
val options = target.createParserOptions()
|
// val options = target.createParserOptions()
|
||||||
if (options.isEmpty()) {
|
// if (options.isEmpty()) {
|
||||||
return@doLast
|
// return@doLast
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
val parser = PropertiesParser { target.logger.lifecycle(it) }
|
// val parser = PropertiesParser { target.logger.lifecycle(it) }
|
||||||
|
//
|
||||||
logger.info(
|
// logger.info(
|
||||||
"Running PropertiesParser with arguments: ${options.joinToString(separator = " ")}"
|
// "Running PropertiesParser with arguments: ${options.joinToString(separator = " ")}"
|
||||||
)
|
// )
|
||||||
|
//
|
||||||
val ok = parser.run(options.toTypedArray())
|
// val ok = parser.run(options.toTypedArray())
|
||||||
if (!ok) {
|
// if (!ok) {
|
||||||
throw GradleException("Failed to parse property files")
|
// throw GradleException("Failed to parse property files")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
outputs.dir(target.propsDir)
|
// outputs.dir(target.propsDir)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
target.tasks.register("cleanCompiledProperties", Delete::class.java) { delete(target.propsDir) }
|
// target.tasks.register("cleanCompiledProperties", Delete::class.java) { delete(target.propsDir) }
|
||||||
|
//
|
||||||
target.tasks.withType(JavaCompile::class.java) { dependsOn("compileProperties") }
|
// target.tasks.withType(JavaCompile::class.java) { dependsOn("compileProperties") }
|
||||||
target.tasks.getByName("clean").dependsOn("cleanCompiledProperties")
|
// target.tasks.getByName("clean").dependsOn("cleanCompiledProperties")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ fun Project.configureAndroidModule(
|
|||||||
arrayOf(
|
arrayOf(
|
||||||
"META-INF/CHANGES",
|
"META-INF/CHANGES",
|
||||||
"META-INF/README.md",
|
"META-INF/README.md",
|
||||||
|
"nbjavac.hint"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
pickFirsts.addAll(
|
pickFirsts.addAll(
|
||||||
|
|||||||
+5
-14
@@ -46,20 +46,11 @@ public class PropertiesParser {
|
|||||||
|
|
||||||
public Logger logger;
|
public Logger logger;
|
||||||
|
|
||||||
public PropertiesParser(Logger logger) {
|
private final File baseDir;
|
||||||
|
|
||||||
|
public PropertiesParser(Logger logger, File baseDir) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
this.baseDir = baseDir;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(String msg) {
|
public void info(String msg) {
|
||||||
@@ -86,7 +77,7 @@ public class PropertiesParser {
|
|||||||
File propertyFile = new File(propertyPath);
|
File propertyFile = new File(propertyPath);
|
||||||
String prefix = propertyFile.getName().split("\\.")[0];
|
String prefix = propertyFile.getName().split("\\.")[0];
|
||||||
MessageFile messageFile = new MessageFile(propertyFile, prefix);
|
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) {
|
} catch (Throwable ex) {
|
||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-7
@@ -57,6 +57,8 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
public class ClassGenerator {
|
public class ClassGenerator {
|
||||||
|
|
||||||
|
private final File baseDir;
|
||||||
|
|
||||||
private static final String TEMPLATE_PROPS = "#\n" +
|
private static final String TEMPLATE_PROPS = "#\n" +
|
||||||
"# Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.\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" +
|
"# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n" +
|
||||||
@@ -86,10 +88,10 @@ public class ClassGenerator {
|
|||||||
" package {0};\\n\\\n" +
|
" package {0};\\n\\\n" +
|
||||||
" \\n\\\n" +
|
" \\n\\\n" +
|
||||||
" {1}\\n\\\n" +
|
" {1}\\n\\\n" +
|
||||||
" import openjdk.tools.javac.util.JCDiagnostic.Error;\\n\\\n" +
|
" import com.itsaky.androidide.com.sun.tools.javac.util.JCDiagnostic.Error;\\n\\\n" +
|
||||||
" import openjdk.tools.javac.util.JCDiagnostic.Warning;\\n\\\n" +
|
" import com.itsaky.androidide.com.sun.tools.javac.util.JCDiagnostic.Warning;\\n\\\n" +
|
||||||
" import openjdk.tools.javac.util.JCDiagnostic.Note;\\n\\\n" +
|
" import com.itsaky.androidide.com.sun.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.Fragment;\\n\\\n" +
|
||||||
" \\n\\\n" +
|
" \\n\\\n" +
|
||||||
" public class {2} '{'\\n\\\n" +
|
" public class {2} '{'\\n\\\n" +
|
||||||
" {3}\\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.
|
* Form the name of the toplevel factory class.
|
||||||
*/
|
*/
|
||||||
@@ -337,9 +343,7 @@ public class ClassGenerator {
|
|||||||
*/
|
*/
|
||||||
String packageName(File file) {
|
String packageName(File file) {
|
||||||
String path = file.getAbsolutePath();
|
String path = file.getAbsolutePath();
|
||||||
int begin = path.indexOf("src/main/resources".replace('/', File.separatorChar))
|
String packagePath = path.substring(baseDir.getAbsolutePath().length() + 1, path.lastIndexOf(File.separatorChar));
|
||||||
+ "src/main/resources".length();
|
|
||||||
String packagePath = path.substring(begin + 1, path.lastIndexOf(File.separatorChar));
|
|
||||||
return packagePath.replace(File.separatorChar, '.');
|
return packagePath.replace(File.separatorChar, '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-18
@@ -46,30 +46,30 @@ public interface MessageType {
|
|||||||
*/
|
*/
|
||||||
enum SimpleType implements 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),
|
BOOLEAN("boolean", "boolean", null),
|
||||||
COLLECTION("collection", "Collection", "java.util"),
|
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),
|
FRAGMENT("fragment", "Fragment", null),
|
||||||
DIAGNOSTIC("diagnostic", "JCDiagnostic", "openjdk.tools.javac.util"),
|
DIAGNOSTIC("diagnostic", "JCDiagnostic", "com.itsaky.androidide.com.sun.tools.javac.util"),
|
||||||
MODIFIER("modifier", "Modifier", "jdkx.lang.model.element"),
|
MODIFIER("modifier", "Modifier", "com.itsaky.androidide.javax.lang.model.element"),
|
||||||
FILE("file", "File", "java.io"),
|
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"),
|
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),
|
NUMBER("number", "int", null),
|
||||||
OPTION_NAME("option name", "Option", "openjdk.tools.javac.main"),
|
OPTION_NAME("option name", "Option", "com.itsaky.androidide.com.sun.tools.javac.main"),
|
||||||
PROFILE("profile", "Profile", "openjdk.tools.javac.jvm"),
|
PROFILE("profile", "Profile", "com.itsaky.androidide.com.sun.tools.javac.jvm"),
|
||||||
SOURCE("source", "Source", "openjdk.tools.javac.code"),
|
SOURCE("source", "Source", "com.itsaky.androidide.com.sun.tools.javac.code"),
|
||||||
SOURCE_VERSION("source version", "SourceVersion", "jdkx.lang.model"),
|
SOURCE_VERSION("source version", "SourceVersion", "com.itsaky.androidide.javax.lang.model"),
|
||||||
STRING("string", "String", null),
|
STRING("string", "String", null),
|
||||||
SYMBOL("symbol", "Symbol", "openjdk.tools.javac.code"),
|
SYMBOL("symbol", "Symbol", "com.itsaky.androidide.com.sun.tools.javac.code"),
|
||||||
SYMBOL_KIND("symbol kind", "Kind", "openjdk.tools.javac.code.Kinds"),
|
SYMBOL_KIND("symbol kind", "Kind", "com.itsaky.androidide.com.sun.tools.javac.code.Kinds"),
|
||||||
KIND_NAME("kind name", "KindName", "openjdk.tools.javac.code.Kinds"),
|
KIND_NAME("kind name", "KindName", "com.itsaky.androidide.com.sun.tools.javac.code.Kinds"),
|
||||||
TARGET("target", "Target", "openjdk.tools.javac.jvm"),
|
TARGET("target", "Target", "com.itsaky.androidide.com.sun.tools.javac.jvm"),
|
||||||
TOKEN("token", "TokenKind", "openjdk.tools.javac.parser.Tokens"),
|
TOKEN("token", "TokenKind", "com.itsaky.androidide.com.sun.tools.javac.parser.Tokens"),
|
||||||
TREE_TAG("tree tag", "Tag", "openjdk.tools.javac.tree.JCTree"),
|
TREE_TAG("tree tag", "Tag", "com.itsaky.androidide.com.sun.tools.javac.tree.JCTree"),
|
||||||
TYPE("type", "Type", "openjdk.tools.javac.code"),
|
TYPE("type", "Type", "com.itsaky.androidide.com.sun.tools.javac.code"),
|
||||||
URL("url", "URL", "java.net"),
|
URL("url", "URL", "java.net"),
|
||||||
SET("set", "Set", "java.util"),
|
SET("set", "Set", "java.util"),
|
||||||
LIST("list", "List", "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
|
* handy option when prototyping - but usages of custom types should be avoided in product-quality
|
||||||
* resource file comments.
|
* resource file comments.
|
||||||
* <p>
|
* <p>
|
||||||
* Example: 'openjdk.tools.javac.code.Flags.Flag'
|
* Example: 'com.itsaky.androidide.com.sun.tools.javac.code.Flags.Flag'
|
||||||
*/
|
*/
|
||||||
class CustomType implements MessageType {
|
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: 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")
|
val digest = MessageDigest.getInstance("SHA-256")
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ maven-publish-plugin = "0.25.3"
|
|||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
# AndroidIDE
|
# 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 = { 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-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" }
|
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" }
|
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" }
|
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]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
@file:Suppress("UnstableApiUsage")
|
@file:Suppress("UnstableApiUsage")
|
||||||
|
|
||||||
|
include(":subprojects:javac-deps")
|
||||||
|
|
||||||
|
|
||||||
|
include(":subprojects:javac-jackpot")
|
||||||
|
|
||||||
|
|
||||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||||
|
|
||||||
pluginManagement {
|
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