CORDA-2961: Migrate the DJVM into its own repository. (#5166)

* Migrate the DJVM into its own repository.

* Update the documentation for DJVM.
This commit is contained in:
Chris Rankin 2019-05-24 15:09:20 +01:00 committed by Rick Parker
parent 5edd732615
commit aa75157273
232 changed files with 10 additions and 15525 deletions

13
djvm/.gitignore vendored
View File

@ -1,13 +0,0 @@
# DJVM-specific files
**/tmp/
*.log
*.log.gz
# IntelliJ
*.iml
*.ipr
*.iws
.idea/
**/out/

View File

@ -1,122 +0,0 @@
buildscript {
ext {
corda_djvm_version = '5.0-SNAPSHOT'
artifactory_contextUrl = 'https://software.r3.com/artifactory'
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id 'net.corda.plugins.publish-utils' version '4.0.44' apply false
id 'com.github.johnrengelman.shadow' version '5.0.0' apply false
id 'com.jfrog.artifactory' version '4.7.3' apply false
id 'com.jfrog.bintray' version '1.4' apply false
id 'com.gradle.build-scan' version '2.2.1'
}
import static org.gradle.api.JavaVersion.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
subprojects {
group 'net.corda'
version corda_djvm_version
repositories {
mavenCentral()
jcenter()
}
tasks.withType(JavaCompile) {
sourceCompatibility = VERSION_1_8
targetCompatibility = VERSION_1_8
options.encoding = 'UTF-8'
}
tasks.withType(KotlinCompile) {
kotlinOptions {
languageVersion = '1.2'
apiVersion = '1.2'
jvmTarget = VERSION_1_8
javaParameters = true // Useful for reflection.
freeCompilerArgs = ['-Xjvm-default=enable']
}
}
tasks.withType(Jar) { task ->
manifest {
attributes('Corda-Vendor': 'Corda Open Source')
attributes('Automatic-Module-Name': "net.corda.${task.project.name.replaceAll('-', '.')}")
}
}
tasks.withType(Test) {
useJUnitPlatform()
// Prevent the project from creating temporary files outside of the build directory.
systemProperty 'java.io.tmpdir', buildDir.absolutePath
}
}
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
bintrayConfig {
user = System.getenv('CORDA_BINTRAY_USER')
key = System.getenv('CORDA_BINTRAY_KEY')
repo = 'corda'
org = 'r3'
licenses = ['Apache-2.0']
vcsUrl = 'https://github.com/corda/corda'
projectUrl = 'https://github.com/corda/corda'
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
publications = [
'corda-djvm',
'corda-djvm-cli'
]
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'
distribution = 'repo'
}
developer {
id = 'R3'
name = 'R3'
email = 'dev@corda.net'
}
}
artifactory {
publish {
contextUrl = artifactory_contextUrl
repository {
repoKey = 'corda-dev'
username = System.getenv('CORDA_ARTIFACTORY_USERNAME')
password = System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
defaults {
// The root project has applied 'publish-utils' but has nothing to publish.
if (project != rootProject) {
publications(project.extensions.publish.name())
}
}
}
}
wrapper {
gradleVersion = "5.4.1"
distributionType = Wrapper.DistributionType.ALL
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
}

View File

@ -1,90 +0,0 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'com.github.johnrengelman.shadow'
id 'net.corda.plugins.publish-utils'
id 'com.jfrog.artifactory'
id 'idea'
}
description 'Corda deterministic JVM sandbox'
repositories {
maven {
url "$artifactory_contextUrl/corda-dev"
}
}
configurations {
testImplementation.extendsFrom shadow
jdkRt.resolutionStrategy {
// Always check the repository for a newer SNAPSHOT.
cacheChangingModulesFor 0, 'seconds'
}
}
dependencies {
shadow "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
shadow "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
shadow "org.slf4j:slf4j-api:$slf4j_version"
// ASM: byte code manipulation library
implementation "org.ow2.asm:asm:$asm_version"
implementation "org.ow2.asm:asm-commons:$asm_version"
// ClassGraph: classpath scanning
shadow "io.github.classgraph:classgraph:$class_graph_version"
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_jupiter_version"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:$junit_platform_version"
// Test utilities
testImplementation "org.assertj:assertj-core:$assertj_version"
testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
jdkRt "net.corda:deterministic-rt:latest.integration"
}
jar.enabled = false
shadowJar {
baseName 'corda-djvm'
classifier ''
relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm'
// These particular classes are only needed to "bootstrap"
// the compilation of the other sandbox classes. At runtime,
// we will generate better versions from deterministic-rt.jar.
exclude 'sandbox/java/lang/Appendable.class'
exclude 'sandbox/java/lang/CharSequence.class'
exclude 'sandbox/java/lang/Character\$Subset.class'
exclude 'sandbox/java/lang/Character\$Unicode*.class'
exclude 'sandbox/java/lang/Comparable.class'
exclude 'sandbox/java/lang/Enum.class'
exclude 'sandbox/java/lang/Iterable.class'
exclude 'sandbox/java/lang/StackTraceElement.class'
exclude 'sandbox/java/lang/StringBuffer.class'
exclude 'sandbox/java/lang/StringBuilder.class'
exclude 'sandbox/java/nio/**'
exclude 'sandbox/java/util/**'
}
assemble.dependsOn shadowJar
tasks.withType(Test) {
systemProperty 'deterministic-rt.path', configurations.jdkRt.asPath
}
artifacts {
publish shadowJar
}
publish {
dependenciesFrom configurations.shadow
name shadowJar.baseName
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}

View File

@ -1,64 +0,0 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'com.github.johnrengelman.shadow'
id 'net.corda.plugins.publish-utils'
id 'com.jfrog.artifactory'
}
description 'Corda deterministic JVM sandbox command-line tool'
ext {
djvmName = 'corda-djvm-cli'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
implementation "org.apache.logging.log4j:log4j-core:$log4j_version"
implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
implementation "info.picocli:picocli:$picocli_version"
implementation project(path: ':djvm', configuration: 'shadow')
}
jar.enabled = false
shadowJar {
baseName djvmName
classifier ''
manifest {
attributes(
'Automatic-Module-Name': 'net.corda.djvm.cli',
'Main-Class': 'net.corda.djvm.tools.cli.Program',
'Build-Date': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
'Class-Path': 'tmp/'
)
}
}
task shadowZip(type: Zip) {
archiveBaseName = djvmName
archiveClassifier = ''
from(shadowJar) {
rename "$djvmName-(.*).jar", "${djvmName}.jar"
}
from('src/shell/') {
fileMode = 0755
}
zip64 true
}
assemble.dependsOn shadowZip
artifacts {
publish shadowZip
}
publish {
dependenciesFrom configurations.shadow
publishSources = false
publishJavadoc = false
name shadowZip.baseName
}

View File

@ -1,39 +0,0 @@
package net.corda.djvm.tools.cli
import picocli.CommandLine.Command
import picocli.CommandLine.Parameters
import java.nio.file.Path
@Command(
name = "build",
description = ["Build one or more Java source files, each implementing the sandbox runnable interface " +
"required for execution in the deterministic sandbox."]
)
@Suppress("KDocMissingDocumentation")
class BuildCommand : CommandBase() {
@Parameters
var files: Array<Path> = emptyArray()
override fun validateArguments() = files.isNotEmpty()
override fun handleCommand(): Boolean {
val codePath = createCodePath()
val files = files.getFileNames { codePath.resolve(it) }
printVerbose("Compiling ${files.joinToString(", ")}...")
ProcessBuilder("javac", "-cp", "tmp:$jarPath", *files).apply {
inheritIO()
environment().putAll(System.getenv())
start().apply {
waitFor()
return (exitValue() == 0).apply {
if (this) {
printInfo("Build succeeded")
}
}
}
}
return true
}
}

View File

@ -1,34 +0,0 @@
package net.corda.djvm.tools.cli
import net.corda.djvm.source.ClassSource
import picocli.CommandLine.Command
import picocli.CommandLine.Parameters
@Command(
name = "check",
description = ["Statically validate that a class or set of classes (and their dependencies) do not violate any " +
"constraints posed by the deterministic sandbox environment."]
)
@Suppress("KDocMissingDocumentation")
class CheckCommand : ClassCommand() {
override val filters: Array<String>
get() = classes
@Parameters(description = ["The partial or fully qualified names of the Java classes to analyse and validate."])
var classes: Array<String> = emptyArray()
override fun printSuccess(classes: List<Class<*>>) {
for (clazz in classes.sortedBy { it.name }) {
printVerbose("Class ${clazz.name} validated")
}
printVerbose()
}
override fun processClasses(classes: List<Class<*>>) {
val sources = classes.map { ClassSource.fromClassName(it.name) }
val summary = executor.validate(*sources.toTypedArray())
printMessages(summary.messages, summary.classOrigins)
}
}

View File

@ -1,204 +0,0 @@
package net.corda.djvm.tools.cli
import net.corda.djvm.SandboxConfiguration
import net.corda.djvm.analysis.AnalysisConfiguration
import net.corda.djvm.analysis.Whitelist
import net.corda.djvm.execution.*
import net.corda.djvm.references.ClassModule
import net.corda.djvm.source.ClassSource
import net.corda.djvm.source.SourceClassLoader
import net.corda.djvm.utilities.Discovery
import djvm.org.objectweb.asm.ClassReader
import picocli.CommandLine.Option
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@Suppress("KDocMissingDocumentation", "MemberVisibilityCanBePrivate")
abstract class ClassCommand : CommandBase() {
@Option(
names = ["-p", "--profile"],
description = ["The execution profile to use (DEFAULT, UNLIMITED, DISABLE_BRANCHING or DISABLE_THROWS)."]
)
var profile: ExecutionProfile = ExecutionProfile.DEFAULT
@Option(names = ["--ignore-rules"], description = ["Disable all rules pertaining to the sandbox."])
var ignoreRules: Boolean = false
@Option(names = ["--ignore-emitters"], description = ["Disable all emitters defined for the sandbox."])
var ignoreEmitters: Boolean = false
@Option(names = ["--ignore-definition-providers"], description = ["Disable all definition providers."])
var ignoreDefinitionProviders: Boolean = false
@Option(names = ["-c", "--classpath"], description = ["Additions to the default class path."], split = ":")
var classPath: Array<Path> = emptyArray()
@Option(names = ["--disable-tracing"], description = ["Disable tracing in the sandbox."])
var disableTracing: Boolean = false
@Option(names = ["--analyze-annotations"], description = ["Analyze all annotations even if they are not " +
"explicitly referenced."])
var analyzeAnnotations: Boolean = false
@Option(
names = ["--prefix-filters"],
description = ["Only record messages matching one of the provided prefixes."],
split = ":"
)
var prefixFilters: Array<String> = emptyArray()
abstract val filters: Array<String>
private val classModule = ClassModule()
private lateinit var classLoader: ClassLoader
protected var executor = SandboxExecutor<Any, Any>(SandboxConfiguration.DEFAULT)
abstract fun processClasses(classes: List<Class<*>>)
open fun printSuccess(classes: List<Class<*>>) {}
override fun validateArguments() = filters.isNotEmpty()
override fun handleCommand(): Boolean {
val configuration = getConfiguration(Whitelist.MINIMAL)
classLoader = SourceClassLoader(getClasspath(), configuration.analysisConfiguration.classResolver)
createExecutor(configuration)
val classes = discoverClasses(filters).onEmpty {
throw Exception("Could not find any classes matching ${filters.joinToString(" ")} on the " +
"system class path")
}
return try {
processClasses(classes)
printSuccess(classes)
true
} catch (exception: Throwable) {
printException(exception)
if (exception is SandboxException) {
printCosts(exception.executionSummary.costs)
}
false
}
}
protected fun printCosts(costs: CostSummary) {
if (disableTracing) {
return
}
printInfo("Runtime Cost Summary:")
printInfo(" - allocations = @|yellow ${costs.allocations}|@")
printInfo(" - invocations = @|yellow ${costs.invocations}|@")
printInfo(" - jumps = @|yellow ${costs.jumps}|@")
printInfo(" - throws = @|yellow ${costs.throws}|@")
printInfo()
}
private fun discoverClasses(filters: Array<String>): List<Class<*>> {
return findDiscoverableRunnables(filters) + findReferencedClasses(filters) + findClassesInJars(filters)
}
private fun findDiscoverableRunnables(filters: Array<String>): List<Class<*>> {
val classes = find<java.util.function.Function<*,*>>()
val applicableFilters = filters
.filter { !isJarFile(it) && !isFullClassName(it) }
val filteredClasses = applicableFilters
.flatMap { filter ->
classes.filter { clazz ->
clazz.name.contains(filter, true)
}
}
if (applicableFilters.isNotEmpty() && filteredClasses.isEmpty()) {
throw Exception("Could not find any classes implementing ${java.util.function.Function::class.java.simpleName} " +
"whose name matches '${applicableFilters.joinToString(" ")}'")
}
if (applicableFilters.isNotEmpty()) {
printVerbose("Class path: $userClassPath")
printVerbose("Discovered runnables on the class path:")
for (clazz in classes) {
printVerbose(" - ${clazz.name}")
}
printVerbose()
}
return filteredClasses
}
private fun findReferencedClasses(filters: Array<String>): List<Class<*>> {
return filters.filter { !isJarFile(it) && isFullClassName(it) }.map {
val className = classModule.getFormattedClassName(it)
printVerbose("Looking up class $className...")
lookUpClass(className)
}
}
private fun findClassesInJars(filters: Array<String>): List<Class<*>> {
return filters.filter { isJarFile(it) }.flatMap { jarFile ->
mutableListOf<Class<*>>().apply {
ClassSource.fromPath(Paths.get(jarFile)).getStreamIterator().forEach {
val reader = ClassReader(it)
val className = classModule.getFormattedClassName(reader.className)
printVerbose("Looking up class $className in $jarFile...")
this.add(lookUpClass(className))
}
}
}
}
private fun lookUpClass(className: String): Class<*> {
return try {
classLoader.loadClass(className)
} catch (exception: NoClassDefFoundError) {
val reference = exception.message?.let {
"referenced class ${classModule.getFormattedClassName(it)} in "
} ?: ""
throw Exception("Unable to load ${reference}type $className (is it present on the class path?)")
} catch (exception: TypeNotPresentException) {
val reference = exception.typeName() ?: ""
throw Exception("Type $reference not present in class $className")
} catch (exception: Throwable) {
throw Exception("Unable to load type $className (is it present on the class path?)")
}
}
private fun isJarFile(filter: String) = Files.exists(Paths.get(filter)) && filter.endsWith(".jar", true)
private fun isFullClassName(filter: String) = filter.count { it == '.' } > 0
private fun getClasspath() =
classPath.toList() + filters.filter { it.endsWith(".jar", true) }.map { Paths.get(it) }
private fun getConfiguration(whitelist: Whitelist): SandboxConfiguration {
return SandboxConfiguration.of(
profile = profile,
rules = if (ignoreRules) { emptyList() } else { Discovery.find() },
emitters = ignoreEmitters.emptyListIfTrueOtherwiseNull(),
definitionProviders = if (ignoreDefinitionProviders) { emptyList() } else { Discovery.find() },
enableTracing = !disableTracing,
analysisConfiguration = AnalysisConfiguration.createRoot(
whitelist = whitelist,
minimumSeverityLevel = level,
analyzeAnnotations = analyzeAnnotations,
prefixFilters = prefixFilters.toList(),
sourceClassLoaderFactory = { classResolver, bootstrapClassLoader ->
SourceClassLoader(getClasspath(), classResolver, bootstrapClassLoader)
}
)
)
}
private fun createExecutor(configuration: SandboxConfiguration) {
executor = SandboxExecutor(configuration)
}
private fun <T> Boolean.emptyListIfTrueOtherwiseNull(): List<T>? = when (this) {
true -> emptyList()
false -> null
}
}

View File

@ -1,279 +0,0 @@
package net.corda.djvm.tools.cli
import net.corda.djvm.analysis.Whitelist
import net.corda.djvm.execution.SandboxException
import net.corda.djvm.messages.MessageCollection
import net.corda.djvm.messages.Severity
import net.corda.djvm.references.ClassReference
import net.corda.djvm.references.EntityReference
import net.corda.djvm.references.MemberReference
import net.corda.djvm.rewiring.SandboxClassLoadingException
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator
import picocli.CommandLine
import picocli.CommandLine.Help.Ansi
import picocli.CommandLine.Option
import java.nio.file.Path
import java.util.concurrent.Callable
@Suppress("KDocMissingDocumentation")
abstract class CommandBase : Callable<Boolean> {
@Option(
names = ["-l", "--level"],
description = ["The minimum severity level to log (TRACE, DEBUG, INFO, WARNING or ERROR."],
converter = [SeverityConverter::class]
)
protected var level: Severity = Severity.WARNING
@Option(
names = ["-q", "--quiet"],
description = ["Only print important messages to standard output."]
)
private var quiet: Boolean = false
@Option(
names = ["-v", "--verbose"],
description = ["Enable verbose logging."]
)
private var verbose: Boolean = false
@Option(
names = ["--debug"],
description = ["Print full stack traces upon error."]
)
private var debug: Boolean = false
@Option(
names = ["--colors"],
description = ["Use colors when printing to terminal."]
)
private var useColors: Boolean = false
@Option(
names = ["--no-colors"],
description = ["Do not use colors when printing to terminal."]
)
private var useNoColors: Boolean = false
@Option(
names = ["--compact"],
description = ["Print compact errors and warnings."]
)
private var compact: Boolean = false
@Option(
names = ["--print-origins"],
description = ["Print origins for errors and warnings."]
)
private var printOrigins: Boolean = false
private val ansi: Ansi
get() = when {
useNoColors -> Ansi.OFF
useColors -> Ansi.ON
else -> Ansi.AUTO
}
class SeverityConverter : CommandLine.ITypeConverter<Severity> {
override fun convert(value: String): Severity {
return try {
when (value.toUpperCase()) {
"INFO" -> Severity.INFORMATIONAL
else -> Severity.valueOf(value.toUpperCase())
}
} catch (exception: Exception) {
val candidates = Severity.values().filter { it.name.startsWith(value, true) }
if (candidates.size == 1) {
candidates.first()
} else {
println("ERROR: Must be one of ${Severity.values().joinToString(", ") { it.name }}")
Severity.INFORMATIONAL
}
}
}
}
override fun call(): Boolean {
if (!validateArguments()) {
CommandLine.usage(this, System.err)
return false
}
if (verbose && quiet) {
printError("Error: Cannot set verbose and quiet modes at the same time")
return false
}
configureLogging()
return try {
handleCommand()
} catch (exception: Throwable) {
printException(exception)
false
}
}
private fun configureLogging() {
val logLevel = when(level) {
Severity.ERROR -> Level.ERROR
Severity.WARNING -> Level.WARN
Severity.INFORMATIONAL -> Level.INFO
Severity.DEBUG -> Level.DEBUG
Severity.TRACE -> Level.TRACE
}
Configurator.setRootLevel(logLevel)
}
protected fun printException(exception: Throwable) = when (exception) {
is SandboxClassLoadingException -> {
printMessages(exception.messages, exception.classOrigins)
printError()
}
is SandboxException -> {
val cause = exception.cause
when (cause) {
is SandboxClassLoadingException -> {
printMessages(cause.messages, cause.classOrigins)
printError()
}
else -> {
if (debug) {
exception.exception.printStackTrace(System.err)
} else {
printError("Error: ${errorMessage(exception.exception)}")
}
printError()
}
}
}
else -> {
if (debug) {
exception.printStackTrace(System.err)
} else {
printError("Error: ${errorMessage(exception)}")
printError()
}
}
}
private fun errorMessage(exception: Throwable): String {
return when (exception) {
is StackOverflowError -> "Stack overflow"
is OutOfMemoryError -> "Out of memory"
is ThreadDeath -> "Thread death"
else -> {
val message = exception.message
when {
message.isNullOrBlank() -> exception.javaClass.simpleName
else -> message!!
}
}
}
}
protected fun printMessages(messages: MessageCollection, origins: Map<String, Set<EntityReference>> = emptyMap()) {
val sortedMessages = messages.sorted()
val errorCount = messages.errorCount.countOf("error")
val warningCount = messages.warningCount.countOf("warning")
printInfo("Found $errorCount and $warningCount")
if (!compact) {
printInfo()
}
var first = true
for (message in sortedMessages) {
val severityColor = message.severity.color ?: "blue"
val location = message.location.format().let {
when {
it.isNotBlank() -> "in $it: "
else -> it
}
}
if (compact) {
printError(" - @|$severityColor ${message.severity}|@ $location${message.message}.")
} else {
if (!first) {
printError()
}
printError(" - @|$severityColor ${message.severity}|@ $location\n ${message.message}.")
}
if (printOrigins) {
val classOrigins = origins[message.location.className.replace("/", ".")] ?: emptySet()
for (classOrigin in classOrigins.groupBy({ it.className }, { it })) {
val count = classOrigin.value.count()
val reference = when (count) {
1 -> classOrigin.value.first()
else -> ClassReference(classOrigin.value.first().className)
}
when (reference) {
is ClassReference ->
printError(" - Reference from ${reference.className}")
is MemberReference ->
printError(" - Reference from ${reference.className}.${reference.memberName}()")
}
}
printError()
}
first = false
}
}
protected open fun handleCommand(): Boolean {
return false
}
protected open fun validateArguments(): Boolean {
return false
}
protected fun printInfo(message: String = "") {
if (!quiet) {
println(ansi.Text(message).toString())
}
}
protected fun printVerbose(message: String = "") {
if (verbose) {
println(ansi.Text(message).toString())
}
}
protected fun printError(message: String = "") {
System.err.println(ansi.Text(message).toString())
}
protected fun printResult(result: Any?) {
printInfo("Execution successful")
printInfo(" - result = $result")
printInfo()
}
protected fun whitelistFromPath(whitelist: Path?): Whitelist {
return whitelist?.let {
if ("$it" == "NONE") {
Whitelist.EMPTY
} else if ("$it" == "ALL") {
Whitelist.EVERYTHING
} else if ("$it" == "LANG") {
Whitelist.MINIMAL
} else {
try {
Whitelist.fromFile(file = it)
} catch (exception: Throwable) {
throw Exception("Failed to load whitelist '$it'", exception)
}
}
} ?: Whitelist.MINIMAL
}
private fun Int.countOf(suffix: String): String {
return this.let {
when (it) {
0 -> "no ${suffix}s"
1 -> "@|yellow 1|@ $suffix"
else -> "@|yellow $it|@ ${suffix}s"
}
}
}
}

View File

@ -1,29 +0,0 @@
package net.corda.djvm.tools.cli
import picocli.CommandLine
import picocli.CommandLine.Command
@Command(
name = "djvm",
versionProvider = VersionProvider::class,
description = ["JVM for running programs in a deterministic sandbox."],
mixinStandardHelpOptions = true,
subcommands = [
BuildCommand::class,
CheckCommand::class,
InspectionCommand::class,
NewCommand::class,
RunCommand::class,
ShowCommand::class,
TreeCommand::class
]
)
@Suppress("KDocMissingDocumentation")
class Commands : CommandBase() {
fun run(args: Array<String>) = when (CommandLine.call(this, System.err, *args)) {
true -> 0
else -> 1
}
}

View File

@ -1,88 +0,0 @@
package net.corda.djvm.tools.cli
import net.corda.djvm.source.ClassSource
import picocli.CommandLine.Command
import picocli.CommandLine.Parameters
import java.nio.file.Files
@Command(
name = "inspect",
description = ["Inspect the transformations that are being applied to classes before they get loaded into " +
"the sandbox."]
)
@Suppress("KDocMissingDocumentation")
class InspectionCommand : ClassCommand() {
override val filters: Array<String>
get() = classes
@Parameters(description = ["The partial or fully qualified names of the Java classes to inspect."])
var classes: Array<String> = emptyArray()
override fun processClasses(classes: List<Class<*>>) {
val sources = classes.map { ClassSource.fromClassName(it.name) }
val (_, messages) = executor.validate(*sources.toTypedArray())
if (messages.isNotEmpty()) {
for (message in messages.sorted()) {
printInfo(" - $message")
}
printInfo()
}
for (classSource in sources) {
val loadedClass = executor.load(classSource)
val sourceClass = createCodePath().resolve("${loadedClass.type.simpleName}.class")
val originalClass = Files.createTempFile("sandbox-", ".java")
val transformedClass = Files.createTempFile("sandbox-", ".java")
printInfo("Class: ${loadedClass.name}")
printVerbose(" - Size of the original byte code: ${Files.size(sourceClass)}")
printVerbose(" - Size of the transformed byte code: ${loadedClass.byteCode.bytes.size}")
printVerbose(" - Original class: $originalClass")
printVerbose(" - Transformed class: $transformedClass")
printInfo()
// Generate byte code dump of the original class
ProcessBuilder("javap", "-c", sourceClass.toString()).apply {
redirectOutput(originalClass.toFile())
environment().putAll(System.getenv())
start().apply {
waitFor()
exitValue()
}
}
// Generate byte code dump of the transformed class
Files.createTempFile("sandbox-", ".class").apply {
Files.write(this, loadedClass.byteCode.bytes)
ProcessBuilder("javap", "-c", this.toString()).apply {
redirectOutput(transformedClass.toFile())
environment().putAll(System.getenv())
start().apply {
waitFor()
exitValue()
}
}
Files.delete(this)
}
// Generate and display the difference between the original and the transformed class
ProcessBuilder(
"git", "diff", originalClass.toString(), transformedClass.toString()
).apply {
inheritIO()
environment().putAll(System.getenv())
start().apply {
waitFor()
exitValue()
}
}
printInfo()
Files.deleteIfExists(originalClass)
Files.deleteIfExists(transformedClass)
}
}
}

View File

@ -1,72 +0,0 @@
package net.corda.djvm.tools.cli
import picocli.CommandLine.*
import java.nio.file.Files
import java.nio.file.Path
@Command(
name = "new",
description = ["Create one or more new Java classes implementing the sandbox runnable interface that is " +
"required for execution in the deterministic sandbox. Each Java file is created using a template, " +
"with class name derived from the provided file name."
],
showDefaultValues = true
)
@Suppress("KDocMissingDocumentation")
class NewCommand : CommandBase() {
@Parameters(description = ["The names of the Java source files that will be created."])
var files: Array<Path> = emptyArray()
@Option(names = ["-f", "--force"], description = ["Forcefully overwrite files if they already exist."])
var force: Boolean = false
@Option(names = ["--from"], description = ["The input type to use for the constructed runnable."])
var fromType: String = "Object"
@Option(names = ["--to"], description = ["The output type to use for the constructed runnable."])
var toType: String = "Object"
@Option(names = ["--return"], description = ["The default return value for the constructed runnable."])
var returnValue: String = "null"
override fun validateArguments() = files.isNotEmpty()
override fun handleCommand(): Boolean {
val codePath = createCodePath()
val files = files.getFiles { codePath.resolve(it) }
for (file in files) {
try {
printVerbose("Creating file '$file'...")
Files.newBufferedWriter(file, *openOptions(force)).use {
it.append(TEMPLATE
.replace("[NAME]", file.baseName)
.replace("[FROM]", fromType)
.replace("[TO]", toType)
.replace("[RETURN]", returnValue))
}
} catch (exception: Throwable) {
throw Exception("Failed to create file '$file'", exception)
}
}
return true
}
companion object {
val TEMPLATE = """
|package net.corda.sandbox;
|
|import java.util.function.Function;
|
|public class [NAME] implements Function<[FROM], [TO]> {
| @Override
| public [TO] apply([FROM] input) {
| return [RETURN];
| }
|}
""".trimMargin()
}
}

View File

@ -1,12 +0,0 @@
@file:JvmName("Program")
package net.corda.djvm.tools.cli
import kotlin.system.exitProcess
/**
* The entry point of the deterministic sandbox tool.
*/
fun main(args: Array<String>) {
exitProcess(Commands().run(args))
}

View File

@ -1,36 +0,0 @@
package net.corda.djvm.tools.cli
import net.corda.djvm.source.ClassSource
import picocli.CommandLine.Command
import picocli.CommandLine.Parameters
@Command(
name = "run",
description = ["Execute runnable in sandbox."],
showDefaultValues = true
)
@Suppress("KDocMissingDocumentation")
class RunCommand : ClassCommand() {
override val filters: Array<String>
get() = classes
@Parameters(description = ["The partial or fully qualified names of the Java classes to run."])
var classes: Array<String> = emptyArray()
override fun processClasses(classes: List<Class<*>>) {
val interfaceName = java.util.function.Function::class.java.simpleName
for (clazz in classes) {
if (!clazz.interfaces.any { it.simpleName == interfaceName }) {
printError("Class is not an instance of $interfaceName; ${clazz.name}")
return
}
printInfo("Running class ${clazz.name}...")
executor.run(ClassSource.fromClassName(clazz.name), Any()).apply {
printResult(result)
printCosts(costs)
}
}
}
}

View File

@ -1,56 +0,0 @@
package net.corda.djvm.tools.cli
import net.corda.djvm.source.ClassSource
import picocli.CommandLine.Command
import picocli.CommandLine.Parameters
import java.nio.file.Files
@Command(
name = "show",
description = ["Show the transformed version of a class as it is prepared for execution in the deterministic " +
"sandbox."]
)
@Suppress("KDocMissingDocumentation")
class ShowCommand : ClassCommand() {
override val filters: Array<String>
get() = classes
@Parameters(description = ["The partial or fully qualified names of the Java classes to inspect."])
var classes: Array<String> = emptyArray()
override fun processClasses(classes: List<Class<*>>) {
val sources = classes.map { ClassSource.fromClassName(it.name) }
val (_, messages) = executor.validate(*sources.toTypedArray())
if (messages.isNotEmpty()) {
for (message in messages.sorted()) {
printInfo(" - $message")
}
printInfo()
}
for (classSource in sources) {
val loadedClass = executor.load(classSource)
printInfo("Class: ${loadedClass.name}")
printVerbose(" - Byte code size: ${loadedClass.byteCode.bytes.size}")
printVerbose(" - Has been modified: ${loadedClass.byteCode.isModified}")
printInfo()
Files.createTempFile("sandbox-", ".class").apply {
Files.write(this, loadedClass.byteCode.bytes)
ProcessBuilder("javap", "-c", this.toString()).apply {
inheritIO()
environment().putAll(System.getenv())
start().apply {
waitFor()
exitValue()
}
}
Files.delete(this)
}
printInfo()
}
}
}

View File

@ -1,33 +0,0 @@
package net.corda.djvm.tools.cli
import picocli.CommandLine.Command
import java.nio.file.Files
@Command(
name = "tree",
description = ["Show the hierarchy of the classes that have been created with the 'new' command."]
)
@Suppress("KDocMissingDocumentation")
class TreeCommand : CommandBase() {
override fun validateArguments() = true
override fun handleCommand(): Boolean {
val path = workingDirectory.resolve("tmp")
if (!Files.exists(path)) {
printError("No classes have been created so far. Run `djvm new` to get started.")
return false
}
ProcessBuilder("find", ".", "-type", "f").apply {
inheritIO()
environment().putAll(System.getenv())
directory(path.toFile())
start().apply {
waitFor()
exitValue()
}
}
return true
}
}

View File

@ -1,102 +0,0 @@
@file:JvmName("Utilities")
package net.corda.djvm.tools.cli
import io.github.classgraph.ClassGraph
import java.lang.reflect.Modifier.isAbstract
import java.lang.reflect.Modifier.isStatic
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
/**
* Get the expanded file name of each path in the provided array.
*/
fun Array<Path>?.getFiles(map: (Path) -> Path = { it }) = (this ?: emptyArray()).map {
val pathString = it.toString()
val path = map(it)
when {
'/' in pathString || '\\' in pathString ->
throw Exception("Please provide a pathless file name")
pathString.endsWith(".java", true) -> path
else -> Paths.get("$path.java")
}
}
/**
* Get the string representation of each expanded file name in the provided array.
*/
fun Array<Path>?.getFileNames(map: (Path) -> Path = { it }) = this.getFiles(map).map {
it.toString()
}.toTypedArray()
/**
* Execute inlined action if the collection is empty.
*/
inline fun <T> List<T>.onEmpty(action: () -> Unit): List<T> {
if (!this.any()) {
action()
}
return this
}
/**
* Execute inlined action if the array is empty.
*/
inline fun <reified T> Array<T>?.onEmpty(action: () -> Unit): Array<T> {
return (this ?: emptyArray()).toList().onEmpty(action).toTypedArray()
}
/**
* Derive the set of [StandardOpenOption]'s to use for a file operation.
*/
fun openOptions(force: Boolean) = if (force) {
arrayOf(StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)
} else {
arrayOf(StandardOpenOption.CREATE_NEW)
}
/**
* Get the path of where any generated code will be placed. Create the directory if it does not exist.
*/
fun createCodePath(): Path {
return Paths.get("tmp", "net", "corda", "sandbox").let {
Files.createDirectories(it)
}
}
/**
* Return the base name of a file (i.e., its name without extension)
*/
val Path.baseName: String
get() = this.fileName.toString()
.replaceAfterLast('.', "")
.removeSuffix(".")
/**
* The path of the executing JAR.
*/
val jarPath: String = object {}.javaClass.protectionDomain.codeSource.location.toURI().path
/**
* The path of the current working directory.
*/
val workingDirectory: Path = Paths.get(System.getProperty("user.dir"))
/**
* The class path for the current execution context.
*/
val userClassPath: String = System.getProperty("java.class.path")
/**
* Get a reference of each concrete class that implements interface or class [T].
*/
inline fun <reified T> find(scanSpec: String = "net/corda/sandbox"): List<Class<*>> {
return ClassGraph()
.whitelistPaths(scanSpec)
.enableAllInfo()
.scan()
.use { it.getClassesImplementing(T::class.java.name).loadClasses(T::class.java) }
.filter { !isAbstract(it.modifiers) && !isStatic(it.modifiers) }
}

View File

@ -1,14 +0,0 @@
package net.corda.djvm.tools.cli
import com.jcabi.manifests.Manifests
import picocli.CommandLine.IVersionProvider
/**
* Get the version number to use for the tool.
*/
@Suppress("KDocMissingDocumentation")
class VersionProvider : IVersionProvider {
override fun getVersion(): Array<String> = arrayOf(
Manifests.read("Corda-Release-Version")
)
}

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info">
<ThresholdFilter level="trace"/>
<Appenders>
<!-- Will generate up to 10 log files for a given day. During every rollover it will delete
those that are older than 60 days, but keep the most recent 10 GB -->
<RollingFile name="RollingFile-Appender"
fileName="djvm.log"
filePattern="djvm.%date{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%date{ISO8601}{UTC}Z [%-5level] %c - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy min="1" max="10">
<Delete basePath="" maxDepth="1">
<IfFileName glob="djvm*.log.gz"/>
<IfLastModified age="60d">
<IfAny>
<IfAccumulatedFileSize exceeds="10 GB"/>
</IfAny>
</IfLastModified>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="RollingFile-Appender"/>
</Root>
</Loggers>
</Configuration>

View File

@ -1,16 +0,0 @@
#!/usr/bin/env bash
SCRIPT_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
CLASSPATH="${CLASSPATH:-}"
DEBUG=`echo "${DEBUG:-0}" | sed 's/^[Nn][Oo]*$/0/g'`
DEBUG_PORT=5005
DEBUG_AGENT=""
if [ "$DEBUG" != 0 ]; then
echo "Opening remote debugging session on port $DEBUG_PORT"
DEBUG_AGENT="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$DEBUG_PORT"
fi
exec java $DEBUG_AGENT -cp "$CLASSPATH:.:tmp:$SCRIPT_DIR/corda-djvm-cli.jar" net.corda.djvm.tools.cli.Program "$@"

View File

@ -1,15 +0,0 @@
@ECHO off
SETLOCAL ENABLEEXTENSIONS
IF NOT DEFINED CLASSPATH (SET CLASSPATH=)
IF DEFINED DEBUG (
SET DEBUG_PORT=5005
SET DEBUG_AGENT=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%DEBUG_PORT%
ECHO Opening remote debugging session on port %DEBUG_PORT%
) ELSE (
SET DEBUG_AGENT=
)
CALL java %DEBUG_AGENT% -cp "%CLASSPATH%;.;tmp;%~dp0\corda-djvm-cli.jar" net.corda.djvm.tools.cli.Program %*

View File

@ -1,7 +0,0 @@
#!/usr/bin/env bash
SCRIPT_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
# Generate auto-completion file for Bash and ZSH
java -cp ${SCRIPT_DIR}/corda-djvm-cli.jar \
picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f

View File

@ -1 +0,0 @@
djvm_completion

View File

@ -1,19 +0,0 @@
#!/usr/bin/env bash
file="${BASH_SOURCE[0]}"
linked_file="$(test -L "$file" && readlink "$file" || echo "$file")"
base_dir="$(cd "$(dirname "$linked_file")/../" && pwd)"
djvm_cli_jar=$(ls -1 $base_dir/cli/build/libs/corda-djvm-cli-*.jar)
CLASSPATH="${CLASSPATH:-}"
DEBUG=`echo "${DEBUG:-0}" | sed 's/^[Nn][Oo]*$/0/g'`
DEBUG_PORT=5005
DEBUG_AGENT=""
if [ "$DEBUG" != 0 ]; then
echo "Opening remote debugging session on port $DEBUG_PORT"
DEBUG_AGENT="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$DEBUG_PORT"
fi
java $DEBUG_AGENT -cp "$CLASSPATH:.:tmp:$djvm_cli_jar" net.corda.djvm.tools.cli.Program "$@"

View File

@ -1,24 +0,0 @@
#!/usr/bin/env bash
file="${BASH_SOURCE[0]}"
base_dir="$(cd "$(dirname "$file")/" && pwd)"
# Build DJVM module and CLI
cd "$base_dir/.."
if !(../gradlew shadowJar); then
echo "Failed to build DJVM"
exit 1
fi
djvm_cli_jar=$(ls -1 $base_dir/../cli/build/libs/corda-djvm-cli-*.jar)
# Generate auto-completion file for Bash and ZSH
cd "$base_dir"
if !(java -cp $djvm_cli_jar \
picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f); then
echo "Failed to generate auto-completion file"
exit 1
fi
# Create a symbolic link to the `djvm` utility
sudo ln -sf "$base_dir/djvm" /usr/local/bin/djvm

View File

@ -1,19 +0,0 @@
package sandbox.java.lang;
import java.io.IOException;
/**
* This is a dummy class that implements just enough of {@link java.lang.Appendable}
* to keep {@link sandbox.java.lang.StringBuilder}, {@link sandbox.java.lang.StringBuffer}
* and {@link sandbox.java.lang.String} honest.
* Note that it does not extend {@link java.lang.Appendable}.
*/
public interface Appendable {
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(CharSequence csq) throws IOException;
Appendable append(char c) throws IOException;
}

View File

@ -1,100 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Boolean extends Object implements Comparable<Boolean>, Serializable {
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
@SuppressWarnings("unchecked")
public static final Class<Boolean> TYPE = (Class) java.lang.Boolean.TYPE;
private final boolean value;
public Boolean(boolean value) {
this.value = value;
}
public Boolean(String s) {
this(parseBoolean(s));
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Boolean) && ((Boolean) other).value == value;
}
@Override
public int hashCode() {
return hashCode(value);
}
public static int hashCode(boolean value) {
return java.lang.Boolean.hashCode(value);
}
public boolean booleanValue() {
return value;
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Boolean.toString(value);
}
@Override
@NotNull
public String toDJVMString() {
return toString(value);
}
public static String toString(boolean b) {
return String.valueOf(b);
}
@Override
@NotNull
java.lang.Boolean fromDJVM() {
return value;
}
@Override
public int compareTo(@NotNull Boolean other) {
return compare(value, other.value);
}
public static int compare(boolean x, boolean y) {
return java.lang.Boolean.compare(x, y);
}
public static boolean parseBoolean(String s) {
return java.lang.Boolean.parseBoolean(String.fromDJVM(s));
}
public static Boolean valueOf(boolean b) {
return b ? TRUE : FALSE;
}
public static Boolean valueOf(String s) {
return valueOf(parseBoolean(s));
}
public static boolean logicalAnd(boolean a, boolean b) {
return java.lang.Boolean.logicalAnd(a, b);
}
public static boolean logicalOr(boolean a, boolean b) {
return java.lang.Boolean.logicalOr(a, b);
}
public static boolean logicalXor(boolean a, boolean b) {
return java.lang.Boolean.logicalXor(a, b);
}
public static Boolean toDJVM(java.lang.Boolean b) { return (b == null) ? null : new Boolean(b); }
}

View File

@ -1,129 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Byte extends Number implements Comparable<Byte> {
public static final byte MIN_VALUE = java.lang.Byte.MIN_VALUE;
public static final byte MAX_VALUE = java.lang.Byte.MAX_VALUE;
public static final int BYTES = java.lang.Byte.BYTES;
public static final int SIZE = java.lang.Byte.SIZE;
@SuppressWarnings("unchecked")
public static final Class<Byte> TYPE = (Class) java.lang.Byte.TYPE;
private final byte value;
public Byte(byte value) {
this.value = value;
}
public Byte(String s) throws NumberFormatException {
this.value = parseByte(s);
}
@Override
public byte byteValue() {
return value;
}
@Override
public short shortValue() {
return (short) value;
}
@Override
public int intValue() {
return (int) value;
}
@Override
public long longValue() {
return (long) value;
}
@Override
public float floatValue() {
return (float) value;
}
@Override
public double doubleValue() {
return (double) value;
}
@Override
public int hashCode() {
return hashCode(value);
}
public static int hashCode(byte b) {
return java.lang.Byte.hashCode(b);
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Byte) && ((Byte) other).value == value;
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Byte.toString(value);
}
@Override
@NotNull
java.lang.Byte fromDJVM() {
return value;
}
@Override
public int compareTo(@NotNull Byte other) {
return compare(this.value, other.value);
}
public static int compare(byte x, byte y) {
return java.lang.Byte.compare(x, y);
}
public static String toString(byte b) {
return Integer.toString(b);
}
public static Byte valueOf(byte b) {
return new Byte(b);
}
public static byte parseByte(String s, int radix) throws NumberFormatException {
return java.lang.Byte.parseByte(String.fromDJVM(s), radix);
}
public static byte parseByte(String s) throws NumberFormatException {
return java.lang.Byte.parseByte(String.fromDJVM(s));
}
public static Byte valueOf(String s, int radix) throws NumberFormatException {
return toDJVM(java.lang.Byte.valueOf(String.fromDJVM(s), radix));
}
public static Byte valueOf(String s) throws NumberFormatException {
return toDJVM(java.lang.Byte.valueOf(String.fromDJVM(s)));
}
public static Byte decode(String s) throws NumberFormatException {
return toDJVM(java.lang.Byte.decode(String.fromDJVM(s)));
}
public static int toUnsignedInt(byte b) {
return java.lang.Byte.toUnsignedInt(b);
}
public static long toUnsignedLong(byte b) {
return java.lang.Byte.toUnsignedLong(b);
}
public static Byte toDJVM(java.lang.Byte b) {
return (b == null) ? null : valueOf(b);
}
}

View File

@ -1,21 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
/**
* This is a dummy class that implements just enough of {@link java.lang.CharSequence}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
public interface CharSequence extends java.lang.CharSequence {
@Override
CharSequence subSequence(int start, int end);
@NotNull
String toDJVMString();
@Override
@NotNull
java.lang.String toString();
}

View File

@ -1,481 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Character extends Object implements Comparable<Character>, Serializable {
public static final int MIN_RADIX = java.lang.Character.MIN_RADIX;
public static final int MAX_RADIX = java.lang.Character.MAX_RADIX;
public static final char MIN_VALUE = java.lang.Character.MIN_VALUE;
public static final char MAX_VALUE = java.lang.Character.MAX_VALUE;
@SuppressWarnings("unchecked")
public static final Class<Character> TYPE = (Class) java.lang.Character.TYPE;
public static final byte UNASSIGNED = java.lang.Character.UNASSIGNED;
public static final byte UPPERCASE_LETTER = java.lang.Character.UPPERCASE_LETTER;
public static final byte LOWERCASE_LETTER = java.lang.Character.LOWERCASE_LETTER;
public static final byte TITLECASE_LETTER = java.lang.Character.TITLECASE_LETTER;
public static final byte MODIFIER_LETTER = java.lang.Character.MODIFIER_LETTER;
public static final byte OTHER_LETTER = java.lang.Character.OTHER_LETTER;
public static final byte NON_SPACING_MARK = java.lang.Character.NON_SPACING_MARK;
public static final byte ENCLOSING_MARK = java.lang.Character.ENCLOSING_MARK;
public static final byte COMBINING_SPACING_MARK = java.lang.Character.COMBINING_SPACING_MARK;
public static final byte DECIMAL_DIGIT_NUMBER = java.lang.Character.DECIMAL_DIGIT_NUMBER;
public static final byte LETTER_NUMBER = java.lang.Character.LETTER_NUMBER;
public static final byte OTHER_NUMBER = java.lang.Character.OTHER_NUMBER;
public static final byte SPACE_SEPARATOR = java.lang.Character.SPACE_SEPARATOR;
public static final byte LINE_SEPARATOR = java.lang.Character.LINE_SEPARATOR;
public static final byte PARAGRAPH_SEPARATOR = java.lang.Character.PARAGRAPH_SEPARATOR;
public static final byte CONTROL = java.lang.Character.CONTROL;
public static final byte FORMAT = java.lang.Character.FORMAT;
public static final byte PRIVATE_USE = java.lang.Character.PRIVATE_USE;
public static final byte SURROGATE = java.lang.Character.SURROGATE;
public static final byte DASH_PUNCTUATION = java.lang.Character.DASH_PUNCTUATION;
public static final byte START_PUNCTUATION = java.lang.Character.START_PUNCTUATION;
public static final byte END_PUNCTUATION = java.lang.Character.END_PUNCTUATION;
public static final byte CONNECTOR_PUNCTUATION = java.lang.Character.CONNECTOR_PUNCTUATION;
public static final byte OTHER_PUNCTUATION = java.lang.Character.OTHER_PUNCTUATION;
public static final byte MATH_SYMBOL = java.lang.Character.MATH_SYMBOL;
public static final byte CURRENCY_SYMBOL = java.lang.Character.CURRENCY_SYMBOL;
public static final byte MODIFIER_SYMBOL = java.lang.Character.MODIFIER_SYMBOL;
public static final byte OTHER_SYMBOL = java.lang.Character.OTHER_SYMBOL;
public static final byte INITIAL_QUOTE_PUNCTUATION = java.lang.Character.INITIAL_QUOTE_PUNCTUATION;
public static final byte FINAL_QUOTE_PUNCTUATION = java.lang.Character.FINAL_QUOTE_PUNCTUATION;
public static final byte DIRECTIONALITY_UNDEFINED = java.lang.Character.DIRECTIONALITY_UNDEFINED;
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = java.lang.Character.DIRECTIONALITY_LEFT_TO_RIGHT;
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT;
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC;
public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = java.lang.Character.DIRECTIONALITY_EUROPEAN_NUMBER;
public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = java.lang.Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR;
public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = java.lang.Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR;
public static final byte DIRECTIONALITY_ARABIC_NUMBER = java.lang.Character.DIRECTIONALITY_ARABIC_NUMBER;
public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = java.lang.Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR;
public static final byte DIRECTIONALITY_NONSPACING_MARK = java.lang.Character.DIRECTIONALITY_NONSPACING_MARK;
public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = java.lang.Character.DIRECTIONALITY_BOUNDARY_NEUTRAL;
public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = java.lang.Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR;
public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = java.lang.Character.DIRECTIONALITY_SEGMENT_SEPARATOR;
public static final byte DIRECTIONALITY_WHITESPACE = java.lang.Character.DIRECTIONALITY_WHITESPACE;
public static final byte DIRECTIONALITY_OTHER_NEUTRALS = java.lang.Character.DIRECTIONALITY_OTHER_NEUTRALS;
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = java.lang.Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING;
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = java.lang.Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE;
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING;
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE;
public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = java.lang.Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT;
public static final char MIN_HIGH_SURROGATE = java.lang.Character.MIN_HIGH_SURROGATE;
public static final char MAX_HIGH_SURROGATE = java.lang.Character.MAX_HIGH_SURROGATE;
public static final char MIN_LOW_SURROGATE = java.lang.Character.MIN_LOW_SURROGATE;
public static final char MAX_LOW_SURROGATE = java.lang.Character.MAX_LOW_SURROGATE;
public static final char MIN_SURROGATE = java.lang.Character.MIN_SURROGATE;
public static final char MAX_SURROGATE = java.lang.Character.MAX_SURROGATE;
public static final int MIN_SUPPLEMENTARY_CODE_POINT = java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
public static final int MIN_CODE_POINT = java.lang.Character.MIN_CODE_POINT;
public static final int MAX_CODE_POINT = java.lang.Character.MAX_CODE_POINT;
public static final int BYTES = java.lang.Character.BYTES;
public static final int SIZE = java.lang.Character.SIZE;
private final char value;
public Character(char c) {
this.value = c;
}
public char charValue() {
return this.value;
}
@Override
public int hashCode() {
return hashCode(this.value);
}
public static int hashCode(char value) {
return java.lang.Character.hashCode(value);
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Character) && ((Character) other).value == value;
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Character.toString(value);
}
@Override
@NotNull
public String toDJVMString() {
return toString(value);
}
@Override
@NotNull
java.lang.Character fromDJVM() {
return value;
}
@Override
public int compareTo(@NotNull Character var1) {
return compare(this.value, var1.value);
}
public static int compare(char x, char y) {
return java.lang.Character.compare(x, y);
}
public static String toString(char c) {
return String.toDJVM(java.lang.Character.toString(c));
}
public static Character valueOf(char c) {
return (c <= 127) ? Cache.cache[(int)c] : new Character(c);
}
public static boolean isValidCodePoint(int codePoint) {
return java.lang.Character.isValidCodePoint(codePoint);
}
public static boolean isBmpCodePoint(int codePoint) {
return java.lang.Character.isBmpCodePoint(codePoint);
}
public static boolean isSupplementaryCodePoint(int codePoint) {
return java.lang.Character.isSupplementaryCodePoint(codePoint);
}
public static boolean isHighSurrogate(char ch) {
return java.lang.Character.isHighSurrogate(ch);
}
public static boolean isLowSurrogate(char ch) {
return java.lang.Character.isLowSurrogate(ch);
}
public static boolean isSurrogate(char ch) {
return java.lang.Character.isSurrogate(ch);
}
public static boolean isSurrogatePair(char high, char low) {
return java.lang.Character.isSurrogatePair(high, low);
}
public static int charCount(int codePoint) {
return java.lang.Character.charCount(codePoint);
}
public static int toCodePoint(char high, char low) {
return java.lang.Character.toCodePoint(high, low);
}
public static int codePointAt(CharSequence seq, int index) {
return java.lang.Character.codePointAt(seq, index);
}
public static int codePointAt(char[] a, int index) {
return java.lang.Character.codePointAt(a, index);
}
public static int codePointAt(char[] a, int index, int limit) {
return java.lang.Character.codePointAt(a, index, limit);
}
public static int codePointBefore(CharSequence seq, int index) {
return java.lang.Character.codePointBefore(seq, index);
}
public static int codePointBefore(char[] a, int index) {
return java.lang.Character.codePointBefore(a, index);
}
public static int codePointBefore(char[] a, int index, int limit) {
return java.lang.Character.codePointBefore(a, index, limit);
}
public static char highSurrogate(int codePoint) {
return java.lang.Character.highSurrogate(codePoint);
}
public static char lowSurrogate(int codePoint) {
return java.lang.Character.lowSurrogate(codePoint);
}
public static int toChars(int codePoint, char[] dst, int dstIndex) {
return java.lang.Character.toChars(codePoint, dst, dstIndex);
}
public static char[] toChars(int codePoint) {
return java.lang.Character.toChars(codePoint);
}
public static int codePointCount(CharSequence seq, int beginIndex, int endIndex) {
return java.lang.Character.codePointCount(seq, beginIndex, endIndex);
}
public static int codePointCount(char[] a, int offset, int count) {
return java.lang.Character.codePointCount(a, offset, count);
}
public static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset) {
return java.lang.Character.offsetByCodePoints(seq, index, codePointOffset);
}
public static int offsetByCodePoints(char[] a, int start, int count, int index, int codePointOffset) {
return java.lang.Character.offsetByCodePoints(a, start, count, index, codePointOffset);
}
public static boolean isLowerCase(char ch) {
return java.lang.Character.isLowerCase(ch);
}
public static boolean isLowerCase(int codePoint) {
return java.lang.Character.isLowerCase(codePoint);
}
public static boolean isUpperCase(char ch) {
return java.lang.Character.isUpperCase(ch);
}
public static boolean isUpperCase(int codePoint) {
return java.lang.Character.isUpperCase(codePoint);
}
public static boolean isTitleCase(char ch) {
return java.lang.Character.isTitleCase(ch);
}
public static boolean isTitleCase(int codePoint) {
return java.lang.Character.isTitleCase(codePoint);
}
public static boolean isDigit(char ch) {
return java.lang.Character.isDigit(ch);
}
public static boolean isDigit(int codePoint) {
return java.lang.Character.isDigit(codePoint);
}
public static boolean isDefined(char ch) {
return java.lang.Character.isDefined(ch);
}
public static boolean isDefined(int codePoint) {
return java.lang.Character.isDefined(codePoint);
}
public static boolean isLetter(char ch) {
return java.lang.Character.isLetter(ch);
}
public static boolean isLetter(int codePoint) {
return java.lang.Character.isLetter(codePoint);
}
public static boolean isLetterOrDigit(char ch) {
return java.lang.Character.isLetterOrDigit(ch);
}
public static boolean isLetterOrDigit(int codePoint) {
return java.lang.Character.isLetterOrDigit(codePoint);
}
@Deprecated
public static boolean isJavaLetter(char ch) {
return java.lang.Character.isJavaLetter(ch);
}
@Deprecated
public static boolean isJavaLetterOrDigit(char ch) {
return java.lang.Character.isJavaLetterOrDigit(ch);
}
public static boolean isAlphabetic(int codePoint) {
return java.lang.Character.isAlphabetic(codePoint);
}
public static boolean isIdeographic(int codePoint) {
return java.lang.Character.isIdeographic(codePoint);
}
public static boolean isJavaIdentifierStart(char ch) {
return java.lang.Character.isJavaIdentifierStart(ch);
}
public static boolean isJavaIdentifierStart(int codePoint) {
return java.lang.Character.isJavaIdentifierStart(codePoint);
}
public static boolean isJavaIdentifierPart(char ch) {
return java.lang.Character.isJavaIdentifierPart(ch);
}
public static boolean isJavaIdentifierPart(int codePoint) {
return java.lang.Character.isJavaIdentifierPart(codePoint);
}
public static boolean isUnicodeIdentifierStart(char ch) {
return java.lang.Character.isUnicodeIdentifierStart(ch);
}
public static boolean isUnicodeIdentifierStart(int codePoint) {
return java.lang.Character.isUnicodeIdentifierStart(codePoint);
}
public static boolean isUnicodeIdentifierPart(char ch) {
return java.lang.Character.isUnicodeIdentifierPart(ch);
}
public static boolean isUnicodeIdentifierPart(int codePoint) {
return java.lang.Character.isUnicodeIdentifierPart(codePoint);
}
public static boolean isIdentifierIgnorable(char ch) {
return java.lang.Character.isIdentifierIgnorable(ch);
}
public static boolean isIdentifierIgnorable(int codePoint) {
return java.lang.Character.isIdentifierIgnorable(codePoint);
}
public static char toLowerCase(char ch) {
return java.lang.Character.toLowerCase(ch);
}
public static int toLowerCase(int codePoint) {
return java.lang.Character.toLowerCase(codePoint);
}
public static char toUpperCase(char ch) {
return java.lang.Character.toUpperCase(ch);
}
public static int toUpperCase(int codePoint) {
return java.lang.Character.toUpperCase(codePoint);
}
public static char toTitleCase(char ch) {
return java.lang.Character.toTitleCase(ch);
}
public static int toTitleCase(int codePoint) {
return java.lang.Character.toTitleCase(codePoint);
}
public static int digit(char ch, int radix) {
return java.lang.Character.digit(ch, radix);
}
public static int digit(int codePoint, int radix) {
return java.lang.Character.digit(codePoint, radix);
}
public static int getNumericValue(char ch) {
return java.lang.Character.getNumericValue(ch);
}
public static int getNumericValue(int codePoint) {
return java.lang.Character.getNumericValue(codePoint);
}
@Deprecated
public static boolean isSpace(char ch) {
return java.lang.Character.isSpace(ch);
}
public static boolean isSpaceChar(char ch) {
return java.lang.Character.isSpaceChar(ch);
}
public static boolean isSpaceChar(int codePoint) {
return java.lang.Character.isSpaceChar(codePoint);
}
public static boolean isWhitespace(char ch) {
return java.lang.Character.isWhitespace(ch);
}
public static boolean isWhitespace(int codePoint) {
return java.lang.Character.isWhitespace(codePoint);
}
public static boolean isISOControl(char ch) {
return java.lang.Character.isISOControl(ch);
}
public static boolean isISOControl(int codePoint) {
return java.lang.Character.isISOControl(codePoint);
}
public static int getType(char ch) {
return java.lang.Character.getType(ch);
}
public static int getType(int codePoint) {
return java.lang.Character.getType(codePoint);
}
public static char forDigit(int digit, int radix) {
return java.lang.Character.forDigit(digit, radix);
}
public static byte getDirectionality(char ch) {
return java.lang.Character.getDirectionality(ch);
}
public static byte getDirectionality(int codePoint) {
return java.lang.Character.getDirectionality(codePoint);
}
public static boolean isMirrored(char ch) {
return java.lang.Character.isMirrored(ch);
}
public static boolean isMirrored(int codePoint) {
return java.lang.Character.isMirrored(codePoint);
}
public static String getName(int codePoint) {
return String.toDJVM(java.lang.Character.getName(codePoint));
}
public static Character toDJVM(java.lang.Character c) {
return (c == null) ? null : valueOf(c);
}
// These three nested classes are placeholders to ensure that
// the Character class bytecode is generated correctly. The
// real classes will be loaded from the from the bootstrap jar
// and then mapped into the sandbox.* namespace.
public static final class UnicodeScript extends Enum<UnicodeScript> {
private UnicodeScript(String name, int index) {
super(name, index);
}
@Override
public int compareTo(@NotNull UnicodeScript other) {
throw new UnsupportedOperationException("Bootstrap implementation");
}
}
public static final class UnicodeBlock extends Subset {}
public static class Subset extends Object {}
/**
* Keep pre-allocated instances of the first 128 characters
* on the basis that these will be used most frequently.
*/
private static class Cache {
private static final Character[] cache = new Character[128];
static {
for (int c = 0; c < cache.length; ++c) {
cache[c] = new Character((char) c);
}
}
private Cache() {}
}
}

View File

@ -1,8 +0,0 @@
package sandbox.java.lang;
/**
* This is a dummy class that implements just enough of {@link java.lang.Comparable}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
public interface Comparable<T> extends java.lang.Comparable<T> {
}

View File

@ -1,37 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
/**
* Pinned exceptions inherit from {@link java.lang.Throwable}, but we
* still need to be able to pass them through the sandbox's
* exception handlers. In which case we will wrap them inside
* one of these.
*
* Exceptions wrapped inside one of these cannot be caught.
*
* Also used for passing exceptions through finally blocks without
* any expensive unwrapping to {@link sandbox.java.lang.Throwable}
* based types.
*/
final class DJVMThrowableWrapper extends Throwable {
private final java.lang.Throwable throwable;
DJVMThrowableWrapper(java.lang.Throwable t) {
throwable = t;
}
/**
* Prevent this wrapper from creating its own stack trace.
*/
@Override
public final Throwable fillInStackTrace() {
return this;
}
@Override
@NotNull
final java.lang.Throwable fromDJVM() {
return throwable;
}
}

View File

@ -1,163 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Double extends Number implements Comparable<Double> {
public static final double POSITIVE_INFINITY = java.lang.Double.POSITIVE_INFINITY;
public static final double NEGATIVE_INFINITY = java.lang.Double.NEGATIVE_INFINITY;
public static final double NaN = java.lang.Double.NaN;
public static final double MAX_VALUE = java.lang.Double.MAX_VALUE;
public static final double MIN_NORMAL = java.lang.Double.MIN_NORMAL;
public static final double MIN_VALUE = java.lang.Double.MIN_VALUE;
public static final int MAX_EXPONENT = java.lang.Double.MAX_EXPONENT;
public static final int MIN_EXPONENT = java.lang.Double.MIN_EXPONENT;
public static final int BYTES = java.lang.Double.BYTES;
public static final int SIZE = java.lang.Double.SIZE;
@SuppressWarnings("unchecked")
public static final Class<Double> TYPE = (Class) java.lang.Double.TYPE;
private final double value;
public Double(double value) {
this.value = value;
}
public Double(String s) throws NumberFormatException {
this.value = parseDouble(s);
}
@Override
public double doubleValue() {
return value;
}
@Override
public float floatValue() {
return (float)value;
}
@Override
public long longValue() {
return (long)value;
}
@Override
public int intValue() {
return (int)value;
}
@Override
public short shortValue() {
return (short)value;
}
@Override
public byte byteValue() {
return (byte)value;
}
public boolean isNaN() {
return java.lang.Double.isNaN(value);
}
public boolean isInfinite() {
return isInfinite(this.value);
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Double) && doubleToLongBits(((Double)other).value) == doubleToLongBits(value);
}
@Override
public int hashCode() {
return hashCode(value);
}
public static int hashCode(double d) {
return java.lang.Double.hashCode(d);
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Double.toString(value);
}
@Override
@NotNull
java.lang.Double fromDJVM() {
return value;
}
@Override
public int compareTo(@NotNull Double other) {
return compare(this.value, other.value);
}
public static String toString(double d) {
return String.toDJVM(java.lang.Double.toString(d));
}
public static String toHexString(double d) {
return String.toDJVM(java.lang.Double.toHexString(d));
}
public static Double valueOf(String s) throws NumberFormatException {
return toDJVM(java.lang.Double.valueOf(String.fromDJVM(s)));
}
public static Double valueOf(double d) {
return new Double(d);
}
public static double parseDouble(String s) throws NumberFormatException {
return java.lang.Double.parseDouble(String.fromDJVM(s));
}
public static boolean isNaN(double d) {
return java.lang.Double.isNaN(d);
}
public static boolean isInfinite(double d) {
return java.lang.Double.isInfinite(d);
}
public static boolean isFinite(double d) {
return java.lang.Double.isFinite(d);
}
public static long doubleToLongBits(double d) {
return java.lang.Double.doubleToLongBits(d);
}
public static long doubleToRawLongBits(double d) {
return java.lang.Double.doubleToRawLongBits(d);
}
public static double longBitsToDouble(long bits) {
return java.lang.Double.longBitsToDouble(bits);
}
public static int compare(double d1, double d2) {
return java.lang.Double.compare(d1, d2);
}
public static double sum(double a, double b) {
return java.lang.Double.sum(a, b);
}
public static double max(double a, double b) {
return java.lang.Double.max(a, b);
}
public static double min(double a, double b) {
return java.lang.Double.min(a, b);
}
public static Double toDJVM(java.lang.Double d) {
return (d == null) ? null : valueOf(d);
}
}

View File

@ -1,35 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
/**
* This is a dummy class. We will load the actual Enum class at run-time.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>, Serializable {
private final String name;
private final int ordinal;
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String name() {
return name;
}
public int ordinal() {
return ordinal;
}
@Override
@NotNull
final java.lang.Enum<?> fromDJVM() {
throw new UnsupportedOperationException("Dummy implementation");
}
}

View File

@ -1,163 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Float extends Number implements Comparable<Float> {
public static final float POSITIVE_INFINITY = java.lang.Float.POSITIVE_INFINITY;
public static final float NEGATIVE_INFINITY = java.lang.Float.NEGATIVE_INFINITY;
public static final float NaN = java.lang.Float.NaN;
public static final float MAX_VALUE = java.lang.Float.MAX_VALUE;
public static final float MIN_NORMAL = java.lang.Float.MIN_NORMAL;
public static final float MIN_VALUE = java.lang.Float.MIN_VALUE;
public static final int MAX_EXPONENT = java.lang.Float.MAX_EXPONENT;
public static final int MIN_EXPONENT = java.lang.Float.MIN_EXPONENT;
public static final int BYTES = java.lang.Float.BYTES;
public static final int SIZE = java.lang.Float.SIZE;
@SuppressWarnings("unchecked")
public static final Class<Float> TYPE = (Class) java.lang.Float.TYPE;
private final float value;
public Float(float value) {
this.value = value;
}
public Float(String s) throws NumberFormatException {
this.value = parseFloat(s);
}
@Override
public int hashCode() {
return hashCode(value);
}
public static int hashCode(float f) {
return java.lang.Float.hashCode(f);
}
@Override
public boolean equals(java.lang.Object other) {
return other instanceof Float && floatToIntBits(((Float)other).value) == floatToIntBits(this.value);
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Float.toString(value);
}
@Override
@NotNull
java.lang.Float fromDJVM() {
return value;
}
@Override
public double doubleValue() {
return (double)value;
}
@Override
public float floatValue() {
return value;
}
@Override
public long longValue() {
return (long)value;
}
@Override
public int intValue() {
return (int)value;
}
@Override
public short shortValue() {
return (short)value;
}
@Override
public byte byteValue() {
return (byte)value;
}
@Override
public int compareTo(@NotNull Float other) {
return compare(this.value, other.value);
}
public boolean isNaN() {
return isNaN(value);
}
public boolean isInfinite() {
return isInfinite(value);
}
public static String toString(float f) {
return String.valueOf(f);
}
public static String toHexString(float f) {
return String.toDJVM(java.lang.Float.toHexString(f));
}
public static Float valueOf(String s) throws NumberFormatException {
return toDJVM(java.lang.Float.valueOf(String.fromDJVM(s)));
}
public static Float valueOf(float f) {
return new Float(f);
}
public static float parseFloat(String s) throws NumberFormatException {
return java.lang.Float.parseFloat(String.fromDJVM(s));
}
public static boolean isNaN(float f) {
return java.lang.Float.isNaN(f);
}
public static boolean isInfinite(float f) {
return java.lang.Float.isInfinite(f);
}
public static boolean isFinite(float f) {
return java.lang.Float.isFinite(f);
}
public static int floatToIntBits(float f) {
return java.lang.Float.floatToIntBits(f);
}
public static int floatToRawIntBits(float f) {
return java.lang.Float.floatToIntBits(f);
}
public static float intBitsToFloat(int bits) {
return java.lang.Float.intBitsToFloat(bits);
}
public static int compare(float f1, float f2) {
return java.lang.Float.compare(f1, f2);
}
public static float sum(float a, float b) {
return java.lang.Float.sum(a, b);
}
public static float max(float a, float b) {
return java.lang.Float.max(a, b);
}
public static float min(float a, float b) {
return java.lang.Float.min(a, b);
}
public static Float toDJVM(java.lang.Float f) {
return (f == null) ? null : valueOf(f);
}
}

View File

@ -1,241 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Integer extends Number implements Comparable<Integer> {
public static final int MIN_VALUE = java.lang.Integer.MIN_VALUE;
public static final int MAX_VALUE = java.lang.Integer.MAX_VALUE;
public static final int BYTES = java.lang.Integer.BYTES;
public static final int SIZE = java.lang.Integer.SIZE;
static final int[] SIZE_TABLE = new int[] { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, MAX_VALUE };
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class) java.lang.Integer.TYPE;
private final int value;
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
@Override
public int hashCode() {
return Integer.hashCode(value);
}
public static int hashCode(int i) {
return java.lang.Integer.hashCode(i);
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Integer) && (value == ((Integer) other).value);
}
@Override
public int intValue() {
return value;
}
@Override
public long longValue() {
return value;
}
@Override
public short shortValue() {
return (short) value;
}
@Override
public byte byteValue() {
return (byte) value;
}
@Override
public float floatValue() {
return (float) value;
}
@Override
public double doubleValue() {
return (double) value;
}
@Override
public int compareTo(@NotNull Integer other) {
return compare(this.value, other.value);
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Integer.toString(value);
}
@Override
@NotNull
java.lang.Integer fromDJVM() {
return value;
}
public static String toString(int i, int radix) {
return String.toDJVM(java.lang.Integer.toString(i, radix));
}
public static String toUnsignedString(int i, int radix) {
return String.toDJVM(java.lang.Integer.toUnsignedString(i, radix));
}
public static String toHexString(int i) {
return String.toDJVM(java.lang.Integer.toHexString(i));
}
public static String toOctalString(int i) {
return String.toDJVM(java.lang.Integer.toOctalString(i));
}
public static String toBinaryString(int i) {
return String.toDJVM(java.lang.Integer.toBinaryString(i));
}
public static String toString(int i) {
return String.toDJVM(java.lang.Integer.toString(i));
}
public static String toUnsignedString(int i) {
return String.toDJVM(java.lang.Integer.toUnsignedString(i));
}
public static int parseInt(String s, int radix) throws NumberFormatException {
return java.lang.Integer.parseInt(String.fromDJVM(s), radix);
}
public static int parseInt(String s) throws NumberFormatException {
return java.lang.Integer.parseInt(String.fromDJVM(s));
}
public static int parseUnsignedInt(String s, int radix) throws NumberFormatException {
return java.lang.Integer.parseUnsignedInt(String.fromDJVM(s), radix);
}
public static int parseUnsignedInt(String s) throws NumberFormatException {
return java.lang.Integer.parseUnsignedInt(String.fromDJVM(s));
}
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return toDJVM(java.lang.Integer.valueOf(String.fromDJVM(s), radix));
}
public static Integer valueOf(String s) throws NumberFormatException {
return toDJVM(java.lang.Integer.valueOf(String.fromDJVM(s)));
}
public static Integer valueOf(int i) {
return new Integer(i);
}
public static Integer decode(String nm) throws NumberFormatException {
return new Integer(java.lang.Integer.decode(String.fromDJVM(nm)));
}
public static int compare(int x, int y) {
return java.lang.Integer.compare(x, y);
}
public static int compareUnsigned(int x, int y) {
return java.lang.Integer.compareUnsigned(x, y);
}
public static long toUnsignedLong(int x) {
return java.lang.Integer.toUnsignedLong(x);
}
public static int divideUnsigned(int dividend, int divisor) {
return java.lang.Integer.divideUnsigned(dividend, divisor);
}
public static int remainderUnsigned(int dividend, int divisor) {
return java.lang.Integer.remainderUnsigned(dividend, divisor);
}
public static int highestOneBit(int i) {
return java.lang.Integer.highestOneBit(i);
}
public static int lowestOneBit(int i) {
return java.lang.Integer.lowestOneBit(i);
}
public static int numberOfLeadingZeros(int i) {
return java.lang.Integer.numberOfLeadingZeros(i);
}
public static int numberOfTrailingZeros(int i) {
return java.lang.Integer.numberOfTrailingZeros(i);
}
public static int bitCount(int i) {
return java.lang.Integer.bitCount(i);
}
public static int rotateLeft(int i, int distance) {
return java.lang.Integer.rotateLeft(i, distance);
}
public static int rotateRight(int i, int distance) {
return java.lang.Integer.rotateRight(i, distance);
}
public static int reverse(int i) {
return java.lang.Integer.reverse(i);
}
public static int signum(int i) {
return java.lang.Integer.signum(i);
}
public static int reverseBytes(int i) {
return java.lang.Integer.reverseBytes(i);
}
public static int sum(int a, int b) {
return java.lang.Integer.sum(a, b);
}
public static int max(int a, int b) {
return java.lang.Integer.max(a, b);
}
public static int min(int a, int b) {
return java.lang.Integer.min(a, b);
}
public static Integer toDJVM(java.lang.Integer i) {
return (i == null) ? null : valueOf(i);
}
static int stringSize(final int number) {
int i = 0;
while (number > SIZE_TABLE[i]) {
++i;
}
return i + 1;
}
static void getChars(final int number, int index, char[] buffer) {
java.lang.String s = java.lang.Integer.toString(number);
int length = s.length();
while (length > 0) {
buffer[--index] = s.charAt(--length);
}
}
}

View File

@ -1,15 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
/**
* This is a dummy class that implements just enough of {@link java.lang.Iterable}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
public interface Iterable<T> extends java.lang.Iterable<T> {
@Override
@NotNull
Iterator<T> iterator();
}

View File

@ -1,239 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Long extends Number implements Comparable<Long> {
public static final long MIN_VALUE = java.lang.Long.MIN_VALUE;
public static final long MAX_VALUE = java.lang.Long.MAX_VALUE;
public static final int BYTES = java.lang.Long.BYTES;
public static final int SIZE = java.lang.Long.SIZE;
@SuppressWarnings("unchecked")
public static final Class<Long> TYPE = (Class) java.lang.Long.TYPE;
private final long value;
public Long(long value) {
this.value = value;
}
public Long(String s) throws NumberFormatException {
this.value = parseLong(s, 10);
}
@Override
public int hashCode() {
return hashCode(value);
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Long) && ((Long) other).longValue() == value;
}
public static int hashCode(long l) {
return java.lang.Long.hashCode(l);
}
@Override
public int intValue() {
return (int) value;
}
@Override
public long longValue() {
return value;
}
@Override
public short shortValue() {
return (short) value;
}
@Override
public byte byteValue() {
return (byte) value;
}
@Override
public float floatValue() {
return (float) value;
}
@Override
public double doubleValue() {
return (double) value;
}
@Override
public int compareTo(@NotNull Long other) {
return compare(value, other.value);
}
public static int compare(long x, long y) {
return java.lang.Long.compare(x, y);
}
@Override
@NotNull
java.lang.Long fromDJVM() {
return value;
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Long.toString(value);
}
public static String toString(long l) {
return String.toDJVM(java.lang.Long.toString(l));
}
public static String toString(long l, int radix) {
return String.toDJVM(java.lang.Long.toString(l, radix));
}
public static String toUnsignedString(long l, int radix) {
return String.toDJVM(java.lang.Long.toUnsignedString(l, radix));
}
public static String toUnsignedString(long l) {
return String.toDJVM(java.lang.Long.toUnsignedString(l));
}
public static String toHexString(long l) {
return String.toDJVM(java.lang.Long.toHexString(l));
}
public static String toOctalString(long l) {
return String.toDJVM(java.lang.Long.toOctalString(l));
}
public static String toBinaryString(long l) {
return String.toDJVM(java.lang.Long.toBinaryString(l));
}
public static long parseLong(String s, int radix) throws NumberFormatException {
return java.lang.Long.parseLong(String.fromDJVM(s), radix);
}
public static long parseLong(String s) throws NumberFormatException {
return java.lang.Long.parseLong(String.fromDJVM(s));
}
public static long parseUnsignedLong(String s, int radix) throws NumberFormatException {
return java.lang.Long.parseUnsignedLong(String.fromDJVM(s), radix);
}
public static long parseUnsignedLong(String s) throws NumberFormatException {
return java.lang.Long.parseUnsignedLong(String.fromDJVM(s));
}
public static Long valueOf(String s, int radix) throws NumberFormatException {
return toDJVM(java.lang.Long.valueOf(String.fromDJVM(s), radix));
}
public static Long valueOf(String s) throws NumberFormatException {
return toDJVM(java.lang.Long.valueOf(String.fromDJVM(s)));
}
public static Long valueOf(long l) {
return new Long(l);
}
public static Long decode(String s) throws NumberFormatException {
return toDJVM(java.lang.Long.decode(String.fromDJVM(s)));
}
public static int compareUnsigned(long x, long y) {
return java.lang.Long.compareUnsigned(x, y);
}
public static long divideUnsigned(long dividend, long divisor) {
return java.lang.Long.divideUnsigned(dividend, divisor);
}
public static long remainderUnsigned(long dividend, long divisor) {
return java.lang.Long.remainderUnsigned(dividend, divisor);
}
public static long highestOneBit(long l) {
return java.lang.Long.highestOneBit(l);
}
public static long lowestOneBit(long l) {
return java.lang.Long.lowestOneBit(l);
}
public static int numberOfLeadingZeros(long l) {
return java.lang.Long.numberOfLeadingZeros(l);
}
public static int numberOfTrailingZeros(long l) {
return java.lang.Long.numberOfTrailingZeros(l);
}
public static int bitCount(long l) {
return java.lang.Long.bitCount(l);
}
public static long rotateLeft(long i, int distance) {
return java.lang.Long.rotateLeft(i, distance);
}
public static long rotateRight(long i, int distance) {
return java.lang.Long.rotateRight(i, distance);
}
public static long reverse(long l) {
return java.lang.Long.reverse(l);
}
public static int signum(long l) {
return java.lang.Long.signum(l);
}
public static long reverseBytes(long l) {
return java.lang.Long.reverseBytes(l);
}
public static long sum(long a, long b) {
return java.lang.Long.sum(a, b);
}
public static long max(long a, long b) {
return java.lang.Long.max(a, b);
}
public static long min(long a, long b) {
return java.lang.Long.min(a, b);
}
public static Long toDJVM(java.lang.Long l) {
return (l == null) ? null : valueOf(l);
}
static int stringSize(final long number) {
long l = 10;
int i = 1;
while ((i < 19) && (number >= l)) {
l *= 10;
++i;
}
return i;
}
static void getChars(final long number, int index, char[] buffer) {
java.lang.String s = java.lang.Long.toString(number);
int length = s.length();
while (length > 0) {
buffer[--index] = s.charAt(--length);
}
}
}

View File

@ -1,21 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
@SuppressWarnings("unused")
public abstract class Number extends Object implements Serializable {
public abstract double doubleValue();
public abstract float floatValue();
public abstract long longValue();
public abstract int intValue();
public abstract short shortValue();
public abstract byte byteValue();
@Override
@NotNull
public String toDJVMString() {
return String.toDJVM(toString());
}
}

View File

@ -1,70 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import sandbox.net.corda.djvm.rules.RuleViolationError;
public class Object {
@Override
public int hashCode() {
return sandbox.java.lang.System.identityHashCode(this);
}
@Override
@NotNull
public java.lang.String toString() {
return toDJVMString().toString();
}
@NotNull
public String toDJVMString() {
return String.toDJVM("sandbox.java.lang.Object@" + java.lang.Integer.toString(hashCode(), 16));
}
@NotNull
java.lang.Object fromDJVM() {
return this;
}
public static java.lang.Object[] fromDJVM(java.lang.Object[] args) {
if (args == null) {
return null;
}
java.lang.Object[] unwrapped = (java.lang.Object[]) java.lang.reflect.Array.newInstance(
fromDJVM(args.getClass().getComponentType()), args.length
);
int i = 0;
for (java.lang.Object arg : args) {
unwrapped[i] = unwrap(arg);
++i;
}
return unwrapped;
}
private static java.lang.Object unwrap(java.lang.Object arg) {
if (arg instanceof Object) {
return ((Object) arg).fromDJVM();
} else if (java.lang.Object[].class.isAssignableFrom(arg.getClass())) {
return fromDJVM((java.lang.Object[]) arg);
} else {
return arg;
}
}
private static Class<?> fromDJVM(Class<?> type) {
try {
return DJVM.fromDJVMType(type);
} catch (ClassNotFoundException e) {
throw new RuleViolationError(e.getMessage());
}
}
static java.util.Locale fromDJVM(sandbox.java.util.Locale locale) {
return java.util.Locale.forLanguageTag(locale.toLanguageTag().fromDJVM());
}
static java.nio.charset.Charset fromDJVM(sandbox.java.nio.charset.Charset charset) {
return java.nio.charset.Charset.forName(charset.name().fromDJVM());
}
}

View File

@ -1,27 +0,0 @@
package sandbox.java.lang;
@SuppressWarnings("unused")
public final class Runtime extends Object {
private static final Runtime RUNTIME = new Runtime();
private Runtime() {}
public static Runtime getRuntime() {
return RUNTIME;
}
/**
* Everything inside the sandbox is single-threaded.
* @return 1
*/
public int availableProcessors() {
return 1;
}
public void loadLibrary(String libraryName) {}
public void load(String fileName) {}
public void runFinalization() {}
public void gc() {}
}

View File

@ -1,128 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Short extends Number implements Comparable<Short> {
public static final short MIN_VALUE = java.lang.Short.MIN_VALUE;
public static final short MAX_VALUE = java.lang.Short.MAX_VALUE;
public static final int BYTES = java.lang.Short.BYTES;
public static final int SIZE = java.lang.Short.SIZE;
@SuppressWarnings("unchecked")
public static final Class<Short> TYPE = (Class) java.lang.Short.TYPE;
private final short value;
public Short(short value) {
this.value = value;
}
public Short(String s) throws NumberFormatException {
this.value = parseShort(s);
}
@Override
public byte byteValue() {
return (byte)value;
}
@Override
public short shortValue() {
return value;
}
@Override
public int intValue() {
return value;
}
@Override
public long longValue() {
return (long)value;
}
@Override
public float floatValue() {
return (float)value;
}
@Override
public double doubleValue() {
return (double)value;
}
@Override
@NotNull
public java.lang.String toString() {
return java.lang.Integer.toString(value);
}
@Override
@NotNull
java.lang.Short fromDJVM() {
return value;
}
@Override
public int hashCode() {
return hashCode(value);
}
public static int hashCode(short value) {
return java.lang.Short.hashCode(value);
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof Short) && ((Short) other).value == value;
}
public int compareTo(@NotNull Short other) {
return compare(this.value, other.value);
}
public static int compare(short x, short y) {
return java.lang.Short.compare(x, y);
}
public static short reverseBytes(short value) {
return java.lang.Short.reverseBytes(value);
}
public static int toUnsignedInt(short x) {
return java.lang.Short.toUnsignedInt(x);
}
public static long toUnsignedLong(short x) {
return java.lang.Short.toUnsignedLong(x);
}
public static short parseShort(String s, int radix) throws NumberFormatException {
return java.lang.Short.parseShort(String.fromDJVM(s), radix);
}
public static short parseShort(String s) throws NumberFormatException {
return java.lang.Short.parseShort(String.fromDJVM(s));
}
public static Short valueOf(String s, int radix) throws NumberFormatException {
return toDJVM(java.lang.Short.valueOf(String.fromDJVM(s), radix));
}
public static Short valueOf(String s) throws NumberFormatException {
return toDJVM(java.lang.Short.valueOf(String.fromDJVM(s)));
}
public static Short valueOf(short s) {
return new Short(s);
}
public static Short decode(String nm) throws NumberFormatException {
return toDJVM(java.lang.Short.decode(String.fromDJVM(nm)));
}
public static Short toDJVM(java.lang.Short i) {
return (i == null) ? null : valueOf(i);
}
}

View File

@ -1,46 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
/**
* This is a dummy class. We will load the genuine class at runtime.
*/
public final class StackTraceElement extends Object implements java.io.Serializable {
private final String className;
private final String methodName;
private final String fileName;
private final int lineNumber;
public StackTraceElement(String className, String methodName, String fileName, int lineNumber) {
this.className = className;
this.methodName = methodName;
this.fileName = fileName;
this.lineNumber = lineNumber;
}
public String getClassName() {
return className;
}
public String getMethodName() {
return methodName;
}
public String getFileName() {
return fileName;
}
public int getLineNumber() {
return lineNumber;
}
@Override
@NotNull
public String toDJVMString() {
return String.toDJVM(
className.toString() + ':' + methodName.toString()
+ (fileName != null ? '(' + fileName.toString() + ':' + lineNumber + ')' : "")
);
}
}

View File

@ -1,424 +0,0 @@
package sandbox.java.lang;
import net.corda.djvm.SandboxRuntimeContext;
import org.jetbrains.annotations.NotNull;
import sandbox.java.nio.charset.Charset;
import sandbox.java.util.Comparator;
import sandbox.java.util.Locale;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
@SuppressWarnings("unused")
public final class String extends Object implements Comparable<String>, CharSequence, Serializable {
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator extends Object implements Comparator<String>, Serializable {
@Override
public int compare(String s1, String s2) {
return java.lang.String.CASE_INSENSITIVE_ORDER.compare(String.fromDJVM(s1), String.fromDJVM(s2));
}
}
private static final String TRUE = new String("true");
private static final String FALSE = new String("false");
private static final Constructor SHARED;
static {
try {
SHARED = java.lang.String.class.getDeclaredConstructor(char[].class, java.lang.Boolean.TYPE);
SHARED.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
private final java.lang.String value;
public String() {
this.value = "";
}
public String(java.lang.String value) {
this.value = value;
}
public String(char value[]) {
this.value = new java.lang.String(value);
}
public String(char value[], int offset, int count) {
this.value = new java.lang.String(value, offset, count);
}
public String(int[] codePoints, int offset, int count) {
this.value = new java.lang.String(codePoints, offset, count);
}
@Deprecated
public String(byte ascii[], int hibyte, int offset, int count) {
this.value = new java.lang.String(ascii, hibyte, offset, count);
}
@Deprecated
public String(byte ascii[], int hibyte) {
this.value = new java.lang.String(ascii, hibyte);
}
public String(byte bytes[], int offset, int length, String charsetName)
throws UnsupportedEncodingException {
this.value = new java.lang.String(bytes, offset, length, fromDJVM(charsetName));
}
public String(byte bytes[], int offset, int length, Charset charset) {
this.value = new java.lang.String(bytes, offset, length, fromDJVM(charset));
}
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this.value = new java.lang.String(bytes, fromDJVM(charsetName));
}
public String(byte bytes[], Charset charset) {
this.value = new java.lang.String(bytes, fromDJVM(charset));
}
public String(byte bytes[], int offset, int length) {
this.value = new java.lang.String(bytes, offset, length);
}
public String(byte bytes[]) {
this.value = new java.lang.String(bytes);
}
public String(StringBuffer buffer) {
this.value = buffer.toString();
}
public String(StringBuilder builder) {
this.value = builder.toString();
}
String(char[] value, boolean share) {
java.lang.String newValue;
try {
// This is (presumably) an optimisation for memory usage.
newValue = (java.lang.String) SHARED.newInstance(value, share);
} catch (Exception e) {
newValue = new java.lang.String(value);
}
this.value = newValue;
}
@Override
public char charAt(int index) {
return value.charAt(index);
}
@Override
public int length() {
return value.length();
}
public boolean isEmpty() {
return value.isEmpty();
}
public int codePointAt(int index) {
return value.codePointAt(index);
}
public int codePointBefore(int index) {
return value.codePointBefore(index);
}
public int codePointCount(int beginIndex, int endIndex) {
return value.codePointCount(beginIndex, endIndex);
}
public int offsetByCodePoints(int index, int codePointOffset) {
return value.offsetByCodePoints(index, codePointOffset);
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
value.getChars(srcBegin, srcEnd, dst, dstBegin);
}
@Deprecated
public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
value.getBytes(srcBegin, srcEnd, dst, dstBegin);
}
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
return value.getBytes(fromDJVM(charsetName));
}
public byte[] getBytes(Charset charset) {
return value.getBytes(fromDJVM(charset));
}
public byte[] getBytes() {
return value.getBytes();
}
@Override
public boolean equals(java.lang.Object other) {
return (other instanceof String) && ((String) other).value.equals(value);
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
@NotNull
public java.lang.String toString() {
return value;
}
@Override
@NotNull
public String toDJVMString() {
return this;
}
@Override
@NotNull
java.lang.String fromDJVM() {
return value;
}
public boolean contentEquals(StringBuffer sb) {
return value.contentEquals((CharSequence) sb);
}
public boolean contentEquals(CharSequence cs) {
return value.contentEquals(cs);
}
public boolean equalsIgnoreCase(String anotherString) {
return value.equalsIgnoreCase(fromDJVM(anotherString));
}
@Override
public CharSequence subSequence(int start, int end) {
return toDJVM((java.lang.String) value.subSequence(start, end));
}
@Override
public int compareTo(@NotNull String other) {
return value.compareTo(other.toString());
}
public int compareToIgnoreCase(String str) {
return value.compareToIgnoreCase(fromDJVM(str));
}
public boolean regionMatches(int toffset, String other, int ooffset, int len) {
return value.regionMatches(toffset, fromDJVM(other), ooffset, len);
}
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
return value.regionMatches(ignoreCase, toffset, fromDJVM(other), ooffset, len);
}
public boolean startsWith(String prefix, int toffset) {
return value.startsWith(fromDJVM(prefix), toffset);
}
public boolean startsWith(String prefix) {
return value.startsWith(fromDJVM(prefix));
}
public boolean endsWith(String suffix) {
return value.endsWith(fromDJVM(suffix));
}
public int indexOf(int ch) {
return value.indexOf(ch);
}
public int indexOf(int ch, int fromIndex) {
return value.indexOf(ch, fromIndex);
}
public int lastIndexOf(int ch) {
return value.lastIndexOf(ch);
}
public int lastIndexOf(int ch, int fromIndex) {
return value.lastIndexOf(ch, fromIndex);
}
public int indexOf(String str) {
return value.indexOf(fromDJVM(str));
}
public int indexOf(String str, int fromIndex) {
return value.indexOf(fromDJVM(str), fromIndex);
}
public int lastIndexOf(String str) {
return value.lastIndexOf(fromDJVM(str));
}
public int lastIndexOf(String str, int fromIndex) {
return value.lastIndexOf(fromDJVM(str), fromIndex);
}
public String substring(int beginIndex) {
return toDJVM(value.substring(beginIndex));
}
public String substring(int beginIndex, int endIndex) {
return toDJVM(value.substring(beginIndex, endIndex));
}
public String concat(String str) {
return toDJVM(value.concat(fromDJVM(str)));
}
public String replace(char oldChar, char newChar) {
return toDJVM(value.replace(oldChar, newChar));
}
public boolean matches(String regex) {
return value.matches(fromDJVM(regex));
}
public boolean contains(CharSequence s) {
return value.contains(s);
}
public String replaceFirst(String regex, String replacement) {
return toDJVM(value.replaceFirst(fromDJVM(regex), fromDJVM(replacement)));
}
public String replaceAll(String regex, String replacement) {
return toDJVM(value.replaceAll(fromDJVM(regex), fromDJVM(replacement)));
}
public String replace(CharSequence target, CharSequence replacement) {
return toDJVM(value.replace(target, replacement));
}
public String[] split(String regex, int limit) {
return toDJVM(value.split(fromDJVM(regex), limit));
}
public String[] split(String regex) {
return toDJVM(value.split(fromDJVM(regex)));
}
public String toLowerCase(Locale locale) {
return toDJVM(value.toLowerCase(fromDJVM(locale)));
}
public String toLowerCase() {
return toDJVM(value.toLowerCase());
}
public String toUpperCase(Locale locale) {
return toDJVM(value.toUpperCase(fromDJVM(locale)));
}
public String toUpperCase() {
return toDJVM(value.toUpperCase());
}
public String trim() {
return toDJVM(value.trim());
}
public String intern() { return (String) SandboxRuntimeContext.getInstance().intern(value, this); }
public char[] toCharArray() {
return value.toCharArray();
}
public static String format(String format, java.lang.Object... args) {
return toDJVM(java.lang.String.format(fromDJVM(format), fromDJVM(args)));
}
public static String format(Locale locale, String format, java.lang.Object... args) {
return toDJVM(java.lang.String.format(fromDJVM(locale), fromDJVM(format), fromDJVM(args)));
}
public static String join(CharSequence delimiter, CharSequence... elements) {
return toDJVM(java.lang.String.join(delimiter, elements));
}
public static String join(CharSequence delimiter,
Iterable<? extends CharSequence> elements) {
return toDJVM(java.lang.String.join(delimiter, elements));
}
public static String valueOf(java.lang.Object obj) {
return (obj instanceof Object) ? ((Object) obj).toDJVMString() : toDJVM(java.lang.String.valueOf(obj));
}
public static String valueOf(char data[]) {
return toDJVM(java.lang.String.valueOf(data));
}
public static String valueOf(char data[], int offset, int count) {
return toDJVM(java.lang.String.valueOf(data, offset, count));
}
public static String copyValueOf(char data[], int offset, int count) {
return toDJVM(java.lang.String.copyValueOf(data, offset, count));
}
public static String copyValueOf(char data[]) {
return toDJVM(java.lang.String.copyValueOf(data));
}
public static String valueOf(boolean b) {
return b ? TRUE : FALSE;
}
public static String valueOf(char c) {
return toDJVM(java.lang.String.valueOf(c));
}
public static String valueOf(int i) {
return toDJVM(java.lang.String.valueOf(i));
}
public static String valueOf(long l) {
return toDJVM(java.lang.String.valueOf(l));
}
public static String valueOf(float f) {
return toDJVM(java.lang.String.valueOf(f));
}
public static String valueOf(double d) {
return toDJVM(java.lang.String.valueOf(d));
}
static String[] toDJVM(java.lang.String[] value) {
if (value == null) {
return null;
}
String[] result = new String[value.length];
int i = 0;
for (java.lang.String v : value) {
result[i] = toDJVM(v);
++i;
}
return result;
}
public static String toDJVM(java.lang.String value) {
return (value == null) ? null : new String(value);
}
public static java.lang.String fromDJVM(String value) {
return (value == null) ? null : value.fromDJVM();
}
}

View File

@ -1,20 +0,0 @@
package sandbox.java.lang;
import java.io.Serializable;
/**
* This is a dummy class that implements just enough of {@link java.lang.StringBuffer}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
public abstract class StringBuffer extends Object implements CharSequence, Appendable, Serializable {
@Override
public abstract StringBuffer append(CharSequence seq);
@Override
public abstract StringBuffer append(CharSequence seq, int start, int end);
@Override
public abstract StringBuffer append(char c);
}

View File

@ -1,20 +0,0 @@
package sandbox.java.lang;
import java.io.Serializable;
/**
* This is a dummy class that implements just enough of {@link java.lang.StringBuilder}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
public abstract class StringBuilder extends Object implements Appendable, CharSequence, Serializable {
@Override
public abstract StringBuilder append(CharSequence seq);
@Override
public abstract StringBuilder append(CharSequence seq, int start, int end);
@Override
public abstract StringBuilder append(char c);
}

View File

@ -1,23 +0,0 @@
package sandbox.java.lang;
import net.corda.djvm.SandboxRuntimeContext;
@SuppressWarnings({"WeakerAccess", "unused"})
public final class System extends Object {
private System() {}
public static int identityHashCode(java.lang.Object obj) {
int nativeHashCode = java.lang.System.identityHashCode(obj);
return SandboxRuntimeContext.getInstance().getHashCodeFor(nativeHashCode);
}
public static final String lineSeparator = String.toDJVM("\n");
public static void arraycopy(java.lang.Object src, int srcPos, java.lang.Object dest, int destPos, int length) {
java.lang.System.arraycopy(src, srcPos, dest, destPos, length);
}
public static void runFinalization() {}
public static void gc() {}
}

View File

@ -1,59 +0,0 @@
package sandbox.java.lang;
import sandbox.java.util.function.Supplier;
/**
* Everything inside the sandbox is single-threaded, so this
* implementation of ThreadLocal is sufficient.
* @param <T> Underlying type of this thread-local variable.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class ThreadLocal<T> extends Object {
private T value;
private boolean isSet;
public ThreadLocal() {
}
protected T initialValue() {
return null;
}
public T get() {
if (!isSet) {
set(initialValue());
}
return value;
}
public void set(T value) {
this.value = value;
this.isSet = true;
}
public void remove() {
value = null;
isSet = false;
}
public static <V> ThreadLocal<V> withInitial(Supplier<? extends V> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
// Stub class for compiling ThreadLocal. The sandbox will import the
// actual SuppliedThreadLocal class at run-time. Having said that, we
// still need a working implementation here for the sake of our tests.
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
@Override
protected T initialValue() {
return supplier.get();
}
}
}

View File

@ -1,137 +0,0 @@
package sandbox.java.lang;
import org.jetbrains.annotations.NotNull;
import sandbox.TaskTypes;
import java.io.Serializable;
@SuppressWarnings({"unused", "WeakerAccess"})
public class Throwable extends Object implements Serializable {
private static final StackTraceElement[] NO_STACK_TRACE = new StackTraceElement[0];
private String message;
private Throwable cause;
private StackTraceElement[] stackTrace;
public Throwable() {
this.cause = this;
fillInStackTrace();
}
public Throwable(String message) {
this();
this.message = message;
}
public Throwable(Throwable cause) {
this.cause = cause;
this.message = (cause == null) ? null : cause.toDJVMString();
fillInStackTrace();
}
public Throwable(String message, Throwable cause) {
this.message = message;
this.cause = cause;
fillInStackTrace();
}
protected Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
if (writableStackTrace) {
fillInStackTrace();
} else {
stackTrace = NO_STACK_TRACE;
}
this.message = message;
this.cause = cause;
}
public String getMessage() {
return message;
}
public String getLocalizedMessage() {
return getMessage();
}
public Throwable getCause() {
return (cause == this) ? null : cause;
}
public Throwable initCause(Throwable cause) {
if (this.cause != this) {
throw new java.lang.IllegalStateException(
"Can't overwrite cause with " + java.util.Objects.toString(cause, "a null"), fromDJVM());
}
if (cause == this) {
throw new java.lang.IllegalArgumentException("Self-causation not permitted", fromDJVM());
}
this.cause = cause;
return this;
}
@Override
@NotNull
public String toDJVMString() {
java.lang.String s = getClass().getName();
String localized = getLocalizedMessage();
return String.valueOf((localized != null) ? (s + ": " + localized.toString()) : s);
}
public StackTraceElement[] getStackTrace() {
return (stackTrace == NO_STACK_TRACE) ? stackTrace : stackTrace.clone();
}
public void setStackTrace(StackTraceElement[] stackTrace) {
StackTraceElement[] traceCopy = stackTrace.clone();
for (int i = 0; i < traceCopy.length; ++i) {
if (traceCopy[i] == null) {
throw new java.lang.NullPointerException("stackTrace[" + i + ']');
}
}
this.stackTrace = traceCopy;
}
@SuppressWarnings({"ThrowableNotThrown", "UnusedReturnValue"})
public Throwable fillInStackTrace() {
if (stackTrace == null) {
/*
* We have been invoked from within this exception's constructor.
* Work our way up the stack trace until we find this constructor,
* and then find out who actually invoked it. This is where our
* sandboxed stack trace will start from.
*
* Our stack trace will end at the point where we entered the sandbox.
*/
final java.lang.StackTraceElement[] elements = new java.lang.Throwable().getStackTrace();
final java.lang.String exceptionName = getClass().getName();
int startIdx = 1;
while (startIdx < elements.length && !isConstructorFor(elements[startIdx], exceptionName)) {
++startIdx;
}
while (startIdx < elements.length && isConstructorFor(elements[startIdx], exceptionName)) {
++startIdx;
}
int endIdx = startIdx;
while (endIdx < elements.length && !TaskTypes.isEntryPoint(elements[endIdx])) {
++endIdx;
}
stackTrace = (startIdx == elements.length) ? NO_STACK_TRACE : DJVM.copyToDJVM(elements, startIdx, endIdx);
}
return this;
}
private static boolean isConstructorFor(java.lang.StackTraceElement elt, java.lang.String className) {
return elt.getClassName().equals(className) && elt.getMethodName().equals("<init>");
}
public void printStackTrace() {}
@Override
@NotNull
java.lang.Throwable fromDJVM() {
return DJVM.fromDJVM(this);
}
}

View File

@ -1,18 +0,0 @@
package sandbox.java.nio.charset;
/**
* This is a dummy class that implements just enough of {@link java.nio.charset.Charset}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
@SuppressWarnings("unused")
public abstract class Charset extends sandbox.java.lang.Object {
private final sandbox.java.lang.String canonicalName;
protected Charset(sandbox.java.lang.String canonicalName, sandbox.java.lang.String[] aliases) {
this.canonicalName = canonicalName;
}
public final sandbox.java.lang.String name() {
return canonicalName;
}
}

View File

@ -1,9 +0,0 @@
package sandbox.java.util;
/**
* This is a dummy class that implements just enough of {@link java.util.Comparator}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
@FunctionalInterface
public interface Comparator<T> extends java.util.Comparator<T> {
}

View File

@ -1,13 +0,0 @@
package sandbox.java.util;
/**
* This is a dummy class to bootstrap us into the sandbox.
*/
public class LinkedHashMap<K, V> extends java.util.LinkedHashMap<K, V> implements Map<K, V> {
public LinkedHashMap(int initialSize) {
super(initialSize);
}
public LinkedHashMap() {
}
}

View File

@ -1,9 +0,0 @@
package sandbox.java.util;
/**
* This is a dummy class that implements just enough of {@link java.util.Locale}
* to allow us to compile {@link sandbox.java.lang.String}.
*/
public abstract class Locale extends sandbox.java.lang.Object {
public abstract sandbox.java.lang.String toLanguageTag();
}

View File

@ -1,7 +0,0 @@
package sandbox.java.util;
/**
* This is a dummy class to bootstrap us into the sandbox.
*/
public interface Map<K, V> extends java.util.Map<K, V> {
}

View File

@ -1,10 +0,0 @@
package sandbox.java.util.function;
/**
* This is a dummy class that implements just enough of {@link java.util.function.Function}
* to allow us to compile {@link sandbox.Task}.
*/
@FunctionalInterface
public interface Function<T, R> {
R apply(T item);
}

View File

@ -1,10 +0,0 @@
package sandbox.java.util.function;
/**
* This is a dummy class that implements just enough of @{link java.util.function.Supplier}
* to allow us to compile {@link sandbox.java.lang.ThreadLocal}.
*/
@FunctionalInterface
public interface Supplier<T> {
T get();
}

View File

@ -1,10 +0,0 @@
package sandbox.sun.misc;
import sandbox.java.lang.Enum;
@SuppressWarnings("unused")
public interface JavaLangAccess {
<E extends Enum<E>> E[] getEnumConstantsShared(Class<E> enumClass);
}

View File

@ -1,20 +0,0 @@
package sandbox.sun.misc;
import sandbox.java.lang.Enum;
@SuppressWarnings("unused")
public class SharedSecrets extends sandbox.java.lang.Object {
private static final JavaLangAccess javaLangAccess = new JavaLangAccessImpl();
private static class JavaLangAccessImpl implements JavaLangAccess {
@SuppressWarnings("unchecked")
@Override
public <E extends Enum<E>> E[] getEnumConstantsShared(Class<E> enumClass) {
return (E[]) sandbox.java.lang.DJVM.getEnumConstantsShared(enumClass);
}
}
public static JavaLangAccess getJavaLangAccess() {
return javaLangAccess;
}
}

View File

@ -1,68 +0,0 @@
package net.corda.djvm
import net.corda.djvm.analysis.AnalysisConfiguration
import net.corda.djvm.code.DefinitionProvider
import net.corda.djvm.code.EMIT_TRACING
import net.corda.djvm.code.Emitter
import net.corda.djvm.execution.ExecutionProfile
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.djvm.rules.Rule
import net.corda.djvm.utilities.Discovery
/**
* Configuration to use for the deterministic sandbox.
*
* @property rules The rules to apply during the analysis phase.
* @property emitters The code emitters / re-writers to apply to all loaded classes.
* @property definitionProviders The meta-data providers to apply to class and member definitions.
* @property executionProfile The execution profile to use in the sandbox.
* @property analysisConfiguration The configuration used in the analysis of classes.
* @property parentClassLoader The [SandboxClassLoader] that this sandbox will use as a parent.
*/
class SandboxConfiguration private constructor(
val rules: List<Rule>,
val emitters: List<Emitter>,
val definitionProviders: List<DefinitionProvider>,
val executionProfile: ExecutionProfile,
val analysisConfiguration: AnalysisConfiguration,
val parentClassLoader: SandboxClassLoader?
) {
@Suppress("unused")
companion object {
/**
* Default configuration for the deterministic sandbox.
*/
@JvmField
val DEFAULT = SandboxConfiguration.of()
/**
* Configuration with no emitters, rules, meta-data providers or runtime thresholds.
*/
@JvmField
val EMPTY = SandboxConfiguration.of(
ExecutionProfile.UNLIMITED, emptyList(), emptyList(), emptyList()
)
/**
* Create a sandbox configuration where one or more properties deviates from the default.
*/
fun of(
profile: ExecutionProfile = ExecutionProfile.DEFAULT,
rules: List<Rule> = Discovery.find(),
emitters: List<Emitter>? = null,
definitionProviders: List<DefinitionProvider> = Discovery.find(),
enableTracing: Boolean = true,
analysisConfiguration: AnalysisConfiguration = AnalysisConfiguration.createRoot(),
parentClassLoader: SandboxClassLoader? = null
) = SandboxConfiguration(
executionProfile = profile,
rules = rules,
emitters = (emitters ?: Discovery.find()).filter {
enableTracing || it.priority > EMIT_TRACING
},
definitionProviders = definitionProviders,
analysisConfiguration = analysisConfiguration,
parentClassLoader = parentClassLoader
)
}
}

View File

@ -1,67 +0,0 @@
package net.corda.djvm
import net.corda.djvm.costing.RuntimeCostSummary
import net.corda.djvm.rewiring.SandboxClassLoader
/**
* The context in which a sandboxed operation is run.
*
* @property configuration The configuration of the sandbox.
*/
class SandboxRuntimeContext(val configuration: SandboxConfiguration) {
/**
* The class loader to use inside the sandbox.
*/
val classLoader: SandboxClassLoader = SandboxClassLoader.createFor(configuration)
/**
* A summary of the currently accumulated runtime costs (for, e.g., memory allocations, invocations, etc.).
*/
val runtimeCosts = RuntimeCostSummary(configuration.executionProfile)
private val hashCodes: MutableMap<Int, Int> = mutableMapOf()
private var objectCounter: Int = 0
// TODO Instead of using a magic offset below, one could take in a per-context seed
fun getHashCodeFor(nativeHashCode: Int): Int {
return hashCodes.computeIfAbsent(nativeHashCode) { ++objectCounter + MAGIC_HASH_OFFSET }
}
private val internStrings: MutableMap<String, Any> = mutableMapOf()
fun intern(key: String, value: Any): Any {
return internStrings.computeIfAbsent(key) { value }
}
/**
* Run a set of actions within the provided sandbox context.
*/
fun use(action: SandboxRuntimeContext.() -> Unit) {
SandboxRuntimeContext.instance = this
try {
action(this)
} finally {
threadLocalContext.remove()
}
}
companion object {
private val threadLocalContext = ThreadLocal<SandboxRuntimeContext?>()
private const val MAGIC_HASH_OFFSET = 0xfed_c0de
/**
* When called from within a sandbox, this returns the context for the current sandbox thread.
*/
@JvmStatic
var instance: SandboxRuntimeContext
get() = threadLocalContext.get()
?: throw IllegalStateException("SandboxContext has not been initialized before use")
private set(value) {
threadLocalContext.set(value)
}
}
}

View File

@ -1,350 +0,0 @@
package net.corda.djvm.analysis
import net.corda.djvm.code.EmitterModule
import net.corda.djvm.code.ruleViolationError
import net.corda.djvm.code.thresholdViolationError
import net.corda.djvm.messages.Severity
import net.corda.djvm.references.ClassModule
import net.corda.djvm.references.Member
import net.corda.djvm.references.MemberModule
import net.corda.djvm.references.MethodBody
import net.corda.djvm.source.BootstrapClassLoader
import net.corda.djvm.source.SourceClassLoader
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type
import sandbox.net.corda.djvm.costing.RuntimeCostAccounter
import java.io.Closeable
import java.io.IOException
import java.nio.file.Path
/**
* The configuration to use for an analysis.
*
* @property whitelist The whitelist of class names.
* @property pinnedClasses Classes that have already been declared in the sandbox namespace and that should be
* made available inside the sandboxed environment. These classes belong to the application
* classloader and so are shared across all sandboxes.
* @property classResolver Functionality used to resolve the qualified name and relevant information about a class.
* @property exceptionResolver Resolves the internal names of synthetic exception classes.
* @property minimumSeverityLevel The minimum severity level to log and report.
* @property analyzeAnnotations Analyze annotations despite not being explicitly referenced.
* @property prefixFilters Only record messages where the originating class name matches one of the provided prefixes.
* If none are provided, all messages will be reported.
* @property classModule Module for handling evolution of a class hierarchy during analysis.
* @property memberModule Module for handling the specification and inspection of class members.
* @property bootstrapClassLoader Optional provider for the Java API classes.
* @property supportingClassLoader ClassLoader providing the classes to run inside the sandbox.
* @property isRootConfiguration Effectively, whether we are allowed to close [bootstrapClassLoader].
*/
class AnalysisConfiguration private constructor(
val whitelist: Whitelist,
val pinnedClasses: Set<String>,
val classResolver: ClassResolver,
val exceptionResolver: ExceptionResolver,
val minimumSeverityLevel: Severity,
val analyzeAnnotations: Boolean,
val prefixFilters: List<String>,
val classModule: ClassModule,
val memberModule: MemberModule,
private val bootstrapClassLoader: BootstrapClassLoader?,
val supportingClassLoader: SourceClassLoader,
private val isRootConfiguration: Boolean
) : Closeable {
/**
* These interfaces are modified as they are mapped into the sandbox by
* having their unsandboxed version "stitched in" as a super-interface.
* And in some cases, we need to add some synthetic bridge methods as well.
*/
val stitchedInterfaces: Map<String, List<Member>> get() = STITCHED_INTERFACES
/**
* These classes have extra methods added as they are mapped into the sandbox.
*/
val stitchedClasses: Map<String, List<Member>> get() = STITCHED_CLASSES
@Throws(IOException::class)
override fun close() {
supportingClassLoader.use {
if (isRootConfiguration) {
bootstrapClassLoader?.close()
}
}
}
/**
* Creates a child [AnalysisConfiguration] with this instance as its parent.
* The child inherits the same [whitelist], [pinnedClasses] and [bootstrapClassLoader].
*/
fun createChild(
classPaths: List<Path> = emptyList(),
newMinimumSeverityLevel: Severity?
): AnalysisConfiguration {
return AnalysisConfiguration(
whitelist = whitelist,
pinnedClasses = pinnedClasses,
classResolver = classResolver,
exceptionResolver = exceptionResolver,
minimumSeverityLevel = newMinimumSeverityLevel ?: minimumSeverityLevel,
analyzeAnnotations = analyzeAnnotations,
prefixFilters = prefixFilters,
classModule = classModule,
memberModule = memberModule,
bootstrapClassLoader = bootstrapClassLoader,
supportingClassLoader = SourceClassLoader(classPaths, classResolver, bootstrapClassLoader),
isRootConfiguration = false
)
}
fun isTemplateClass(className: String): Boolean = className in TEMPLATE_CLASSES
fun isPinnedClass(className: String): Boolean = className in pinnedClasses
fun isJvmException(className: String): Boolean = className in JVM_EXCEPTIONS
fun isSandboxClass(className: String): Boolean = className.startsWith(SANDBOX_PREFIX) && !isPinnedClass(className)
companion object {
/**
* The package name prefix to use for classes loaded into a sandbox.
*/
const val SANDBOX_PREFIX: String = "sandbox/"
/**
* These class must belong to the application class loader.
* They should already exist within the sandbox namespace.
*/
private val MANDATORY_PINNED_CLASSES: Set<String> = setOf(
RuntimeCostAccounter.TYPE_NAME,
ruleViolationError,
thresholdViolationError
)
/**
* These classes will be duplicated into every sandbox's
* parent classloader.
*/
private val TEMPLATE_CLASSES: Set<String> = setOf(
java.lang.Boolean::class.java,
java.lang.Byte::class.java,
java.lang.Character::class.java,
java.lang.Double::class.java,
java.lang.Float::class.java,
java.lang.Integer::class.java,
java.lang.Long::class.java,
java.lang.Number::class.java,
java.lang.Runtime::class.java,
java.lang.Short::class.java,
java.lang.String::class.java,
java.lang.String.CASE_INSENSITIVE_ORDER::class.java,
java.lang.System::class.java,
java.lang.ThreadLocal::class.java,
java.lang.Throwable::class.java,
kotlin.Any::class.java,
sun.misc.JavaLangAccess::class.java,
sun.misc.SharedSecrets::class.java
).sandboxed() + setOf(
"sandbox/Task",
"sandbox/TaskTypes",
"sandbox/java/lang/Character\$Cache",
"sandbox/java/lang/DJVM",
"sandbox/java/lang/DJVMException",
"sandbox/java/lang/DJVMThrowableWrapper",
"sandbox/sun/misc/SharedSecrets\$1",
"sandbox/sun/misc/SharedSecrets\$JavaLangAccessImpl"
)
/**
* These exceptions are thrown by the JVM itself, and
* so we need to handle them without wrapping them.
*
* Note that this set is closed, i.e. every one
* of these exceptions' [Throwable] super classes
* is also within this set.
*
* The full list of exceptions is determined by:
* hotspot/src/share/vm/classfile/vmSymbols.hpp
*/
val JVM_EXCEPTIONS: Set<String> = setOf(
java.io.IOException::class.java,
java.lang.AbstractMethodError::class.java,
java.lang.ArithmeticException::class.java,
java.lang.ArrayIndexOutOfBoundsException::class.java,
java.lang.ArrayStoreException::class.java,
java.lang.ClassCastException::class.java,
java.lang.ClassCircularityError::class.java,
java.lang.ClassFormatError::class.java,
java.lang.ClassNotFoundException::class.java,
java.lang.CloneNotSupportedException::class.java,
java.lang.Error::class.java,
java.lang.Exception::class.java,
java.lang.ExceptionInInitializerError::class.java,
java.lang.IllegalAccessError::class.java,
java.lang.IllegalAccessException::class.java,
java.lang.IllegalArgumentException::class.java,
java.lang.IllegalStateException::class.java,
java.lang.IncompatibleClassChangeError::class.java,
java.lang.IndexOutOfBoundsException::class.java,
java.lang.InstantiationError::class.java,
java.lang.InstantiationException::class.java,
java.lang.InternalError::class.java,
java.lang.LinkageError::class.java,
java.lang.NegativeArraySizeException::class.java,
java.lang.NoClassDefFoundError::class.java,
java.lang.NoSuchFieldError::class.java,
java.lang.NoSuchFieldException::class.java,
java.lang.NoSuchMethodError::class.java,
java.lang.NoSuchMethodException::class.java,
java.lang.NullPointerException::class.java,
java.lang.OutOfMemoryError::class.java,
java.lang.ReflectiveOperationException::class.java,
java.lang.RuntimeException::class.java,
java.lang.StackOverflowError::class.java,
java.lang.StringIndexOutOfBoundsException::class.java,
java.lang.ThreadDeath::class.java,
java.lang.Throwable::class.java,
java.lang.UnknownError::class.java,
java.lang.UnsatisfiedLinkError::class.java,
java.lang.UnsupportedClassVersionError::class.java,
java.lang.UnsupportedOperationException::class.java,
java.lang.VerifyError::class.java,
java.lang.VirtualMachineError::class.java
).sandboxed() + setOf(
// Mentioned here to prevent the DJVM from generating a synthetic wrapper.
"sandbox/java/lang/DJVMThrowableWrapper"
)
/**
* These interfaces will be modified as follows when
* added to the sandbox:
*
* <code>interface sandbox.A extends A</code>
*/
private val STITCHED_INTERFACES: Map<String, List<Member>> = listOf(
object : MethodBuilder(
access = ACC_PUBLIC or ACC_SYNTHETIC or ACC_BRIDGE,
className = sandboxed(CharSequence::class.java),
memberName = "subSequence",
descriptor = "(II)Ljava/lang/CharSequence;"
) {
override fun writeBody(emitter: EmitterModule) = with(emitter) {
pushObject(0)
pushInteger(1)
pushInteger(2)
invokeInterface(className, memberName, "(II)L$className;")
returnObject()
}
}.withBody()
.build(),
MethodBuilder(
access = ACC_PUBLIC or ACC_ABSTRACT,
className = sandboxed(CharSequence::class.java),
memberName = "toString",
descriptor = "()Ljava/lang/String;"
).build()
).mapByClassName() + mapOf(
sandboxed(Comparable::class.java) to emptyList(),
sandboxed(Comparator::class.java) to emptyList(),
sandboxed(Iterable::class.java) to emptyList()
)
/**
* These classes have extra methods added when mapped into the sandbox.
*/
private val STITCHED_CLASSES: Map<String, List<Member>> = listOf(
object : MethodBuilder(
access = ACC_FINAL,
className = sandboxed(Enum::class.java),
memberName = "fromDJVM",
descriptor = "()Ljava/lang/Enum;",
signature = "()Ljava/lang/Enum<*>;"
) {
override fun writeBody(emitter: EmitterModule) = with(emitter) {
pushObject(0)
invokeStatic("sandbox/java/lang/DJVM", "fromDJVMEnum", "(Lsandbox/java/lang/Enum;)Ljava/lang/Enum;")
returnObject()
}
}.withBody()
.build(),
object : MethodBuilder(
access = ACC_BRIDGE or ACC_SYNTHETIC,
className = sandboxed(Enum::class.java),
memberName = "fromDJVM",
descriptor = "()Ljava/lang/Object;"
) {
override fun writeBody(emitter: EmitterModule) = with(emitter) {
pushObject(0)
invokeVirtual(className, memberName, "()Ljava/lang/Enum;")
returnObject()
}
}.withBody()
.build()
).mapByClassName()
private fun sandboxed(clazz: Class<*>): String = (SANDBOX_PREFIX + Type.getInternalName(clazz)).intern()
private fun Set<Class<*>>.sandboxed(): Set<String> = map(Companion::sandboxed).toSet()
private fun Iterable<Member>.mapByClassName(): Map<String, List<Member>>
= groupBy(Member::className).mapValues(Map.Entry<String, List<Member>>::value)
/**
* @see [AnalysisConfiguration]
*/
fun createRoot(
whitelist: Whitelist = Whitelist.MINIMAL,
additionalPinnedClasses: Set<String> = emptySet(),
minimumSeverityLevel: Severity = Severity.WARNING,
analyzeAnnotations: Boolean = false,
prefixFilters: List<String> = emptyList(),
classModule: ClassModule = ClassModule(),
memberModule: MemberModule = MemberModule(),
bootstrapClassLoader: BootstrapClassLoader? = null,
sourceClassLoaderFactory: (ClassResolver, BootstrapClassLoader?) -> SourceClassLoader = { classResolver, bootstrapCL ->
SourceClassLoader(emptyList(), classResolver, bootstrapCL)
}
): AnalysisConfiguration {
val pinnedClasses = MANDATORY_PINNED_CLASSES + additionalPinnedClasses
val classResolver = ClassResolver(pinnedClasses, TEMPLATE_CLASSES, whitelist, SANDBOX_PREFIX)
return AnalysisConfiguration(
whitelist = whitelist,
pinnedClasses = pinnedClasses,
classResolver = classResolver,
exceptionResolver = ExceptionResolver(JVM_EXCEPTIONS, pinnedClasses, SANDBOX_PREFIX),
minimumSeverityLevel = minimumSeverityLevel,
analyzeAnnotations = analyzeAnnotations,
prefixFilters = prefixFilters,
classModule = classModule,
memberModule = memberModule,
bootstrapClassLoader = bootstrapClassLoader,
supportingClassLoader = sourceClassLoaderFactory(classResolver, bootstrapClassLoader),
isRootConfiguration = true
)
}
}
private open class MethodBuilder(
protected val access: Int,
protected val className: String,
protected val memberName: String,
protected val descriptor: String,
protected val signature: String = ""
) {
private val bodies = mutableListOf<MethodBody>()
protected open fun writeBody(emitter: EmitterModule) {}
fun withBody(): MethodBuilder {
bodies.add(::writeBody)
return this
}
fun build() = Member(
access = access,
className = className,
memberName = memberName,
signature = descriptor,
genericsDetails = signature,
body = bodies
)
}
}

View File

@ -1,53 +0,0 @@
package net.corda.djvm.analysis
import net.corda.djvm.code.asPackagePath
import net.corda.djvm.messages.MessageCollection
import net.corda.djvm.references.ClassHierarchy
import net.corda.djvm.references.EntityReference
import net.corda.djvm.references.ReferenceMap
/**
* The context in which one or more classes are analysed.
*
* @property messages Collection of messages gathered as part of the analysis.
* @property classes List of class definitions that have been analyzed.
* @property references A collection of all referenced members found during analysis together with the locations from
* where each member has been accessed or invoked.
*/
class AnalysisContext private constructor(
val messages: MessageCollection,
val classes: ClassHierarchy,
val references: ReferenceMap
) {
private val origins = mutableMapOf<String, MutableSet<EntityReference>>()
/**
* Record a class origin in the current analysis context.
*/
fun recordClassOrigin(name: String, origin: EntityReference) {
origins.getOrPut(name.asPackagePath) { mutableSetOf() }.add(origin)
}
/**
* Map of class origins. The resulting set represents the types referencing the class in question.
*/
val classOrigins: Map<String, Set<EntityReference>>
get() = origins
companion object {
/**
* Create a new analysis context from provided configuration.
*/
fun fromConfiguration(configuration: AnalysisConfiguration): AnalysisContext {
return AnalysisContext(
MessageCollection(configuration.minimumSeverityLevel, configuration.prefixFilters),
ClassHierarchy(configuration.classModule, configuration.memberModule),
ReferenceMap(configuration.classModule)
)
}
}
}

View File

@ -1,22 +0,0 @@
package net.corda.djvm.analysis
import net.corda.djvm.messages.MessageCollection
import net.corda.djvm.references.ClassRepresentation
import net.corda.djvm.references.Member
/**
* The context of a class analysis.
*
* @property clazz The class currently being analyzed.
* @property member The member currently being analyzed.
* @property location The current source location.
* @property messages Collection of messages gathered as part of the analysis.
* @property configuration The configuration used in the analysis.
*/
data class AnalysisRuntimeContext(
val clazz: ClassRepresentation,
val member: Member?,
val location: SourceLocation,
val messages: MessageCollection,
val configuration: AnalysisConfiguration
)

View File

@ -1,598 +0,0 @@
package net.corda.djvm.analysis
import net.corda.djvm.code.EmitterModule
import net.corda.djvm.code.Instruction
import net.corda.djvm.code.emptyAsNull
import net.corda.djvm.code.instructions.*
import net.corda.djvm.messages.Message
import net.corda.djvm.references.*
import org.objectweb.asm.*
import java.io.InputStream
/**
* Functionality for traversing a class and its members.
*
* @property configuration The configuration to use for the analysis
* @property classVisitor Class visitor to use when traversing the structure of classes.
*/
open class ClassAndMemberVisitor(
private val configuration: AnalysisConfiguration,
private val classVisitor: ClassVisitor?
) {
/**
* Holds a reference to the currently used analysis context.
*/
protected var analysisContext: AnalysisContext = AnalysisContext.fromConfiguration(configuration)
/**
* Holds a link to the class currently being traversed.
*/
private var currentClass: ClassRepresentation? = null
/**
* Holds a link to the member currently being traversed.
*/
private var currentMember: Member? = null
/**
* The current source location.
*/
private var sourceLocation = SourceLocation()
/**
* Analyze class by using the provided qualified name of the class.
*/
inline fun <reified T> analyze(context: AnalysisContext) = analyze(T::class.java.name, context)
/**
* Analyze class by using the provided qualified name of the class.
*
* @param className The full, qualified name of the class.
* @param context The context in which the analysis is conducted.
* @param origin The originating class for the analysis.
*/
fun analyze(className: String, context: AnalysisContext, origin: String? = null) {
configuration.supportingClassLoader.classReader(className, context, origin).apply {
analyze(this, context)
}
}
/**
* Analyze class by using the provided stream of its byte code.
*
* @param classStream A stream of the class' byte code.
* @param context The context in which the analysis is conducted.
*/
fun analyze(classStream: InputStream, context: AnalysisContext) =
analyze(ClassReader(classStream), context)
/**
* Analyze class by using the provided class reader.
*
* @param classReader An instance of the class reader to use to access the byte code.
* @param context The context in which to analyse the provided class.
* @param options Options for how to parse and process the class.
*/
fun analyze(classReader: ClassReader, context: AnalysisContext, options: Int = 0) {
analysisContext = context
classReader.accept(ClassVisitorImpl(classVisitor), options)
}
/**
* Extract information about the traversed class.
*/
open fun visitClass(clazz: ClassRepresentation): ClassRepresentation = clazz
/**
* Process class after it has been fully traversed and analyzed.
* The [classVisitor] has finished visiting all of the class's
* existing elements (i.e. methods, fields, inner classes etc)
* and is about to complete. However, it can still add new
* elements to the class, if required.
*/
open fun visitClassEnd(classVisitor: ClassVisitor, clazz: ClassRepresentation) {}
/**
* Extract the meta-data indicating the source file of the traversed class (i.e., where it is compiled from).
*/
open fun visitSource(clazz: ClassRepresentation, source: String) {}
/**
* Extract information about the traversed class annotation.
*/
open fun visitClassAnnotation(clazz: ClassRepresentation, descriptor: String) {}
/**
* Extract information about the traversed member annotation.
*/
open fun visitMemberAnnotation(clazz: ClassRepresentation, member: Member, descriptor: String) {}
/**
* Extract information about the traversed method.
*/
open fun visitMethod(clazz: ClassRepresentation, method: Member): Member = method
/**
* Extract information about the traversed field.
*/
open fun visitField(clazz: ClassRepresentation, field: Member): Member = field
/**
* Extract information about the traversed instruction.
*/
open fun visitInstruction(method: Member, emitter: EmitterModule, instruction: Instruction) {}
/**
* Get the analysis context to pass on to method and field visitors.
*/
protected fun currentAnalysisContext(): AnalysisRuntimeContext {
return AnalysisRuntimeContext(
currentClass!!,
currentMember,
sourceLocation,
analysisContext.messages,
configuration
)
}
/**
* Check if a class should be processed or not.
*/
protected fun shouldBeProcessed(className: String): Boolean {
return !configuration.whitelist.inNamespace(className) &&
!configuration.isPinnedClass(className)
}
/**
* Extract information about the traversed member annotation.
*/
private fun visitMemberAnnotation(
descriptor: String,
referencedClass: ClassRepresentation? = null,
referencedMember: Member? = null
) {
val clazz = (referencedClass ?: currentClass) ?: return
val member = (referencedMember ?: currentMember) ?: return
member.annotations.add(descriptor)
captureExceptions {
visitMemberAnnotation(clazz, member, descriptor)
}
}
/**
* Run action with a guard that populates [AnalysisRuntimeContext.messages]
* based on the output.
*/
private inline fun captureExceptions(action: () -> Unit): Boolean {
return try {
action()
true
} catch (exception: Throwable) {
recordMessage(exception, currentAnalysisContext())
false
}
}
/**
* Record a message derived from a [Throwable].
*/
private fun recordMessage(exception: Throwable, context: AnalysisRuntimeContext) {
context.messages.add(Message.fromThrowable(exception, context.location))
}
/**
* Record a reference to a class.
*/
private fun recordTypeReference(type: String) {
val typeName = configuration.classModule
.normalizeClassName(type)
.replace("[]", "")
if (shouldBeProcessed(currentClass!!.name)) {
val classReference = ClassReference(typeName)
analysisContext.references.add(classReference, sourceLocation)
}
}
/**
* Record a reference to a class member.
*/
private fun recordMemberReference(owner: String, name: String, desc: String) {
if (shouldBeProcessed(currentClass!!.name)) {
recordTypeReference(owner)
val memberReference = MemberReference(owner, name, desc)
analysisContext.references.add(memberReference, sourceLocation)
}
}
/**
* Visitor used to traverse and analyze a class.
*/
private inner class ClassVisitorImpl(
targetVisitor: ClassVisitor?
) : ClassVisitor(API_VERSION, targetVisitor) {
/**
* Extract information about the traversed class.
*/
override fun visit(
version: Int, access: Int, name: String, signature: String?, superName: String?,
interfaces: Array<String>?
) {
val superClassName = superName ?: ""
val interfaceNames = interfaces?.toMutableList() ?: mutableListOf()
ClassRepresentation(version, access, name, superClassName, interfaceNames, genericsDetails = signature ?: "").also {
currentClass = it
currentMember = null
sourceLocation = SourceLocation(className = name)
}
captureExceptions {
currentClass = visitClass(currentClass!!)
}
val visitedClass = currentClass!!
analysisContext.classes.add(visitedClass)
super.visit(
version, access, visitedClass.name, signature,
visitedClass.superClass.emptyAsNull,
visitedClass.interfaces.toTypedArray()
)
}
/**
* Post-processing of the traversed class.
*/
override fun visitEnd() {
configuration.classModule
.getClassReferencesFromClass(currentClass!!, configuration.analyzeAnnotations)
.forEach(::recordTypeReference)
captureExceptions {
visitClassEnd(this, currentClass!!)
}
super.visitEnd()
}
/**
* Extract the meta-data indicating the source file of the traversed class (i.e., where it is compiled from).
*/
override fun visitSource(source: String?, debug: String?) {
currentClass!!.apply {
sourceFile = configuration.classModule.getFullSourceLocation(this, source)
sourceLocation = sourceLocation.copy(sourceFile = sourceFile)
captureExceptions {
visitSource(this, sourceFile)
}
}
super.visitSource(source, debug)
}
/**
* Extract information about provided annotations.
*/
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
currentClass!!.apply {
annotations.add(desc)
captureExceptions {
visitClassAnnotation(this, desc)
}
}
return super.visitAnnotation(desc, visible)
}
/**
* Extract information about the traversed method.
*/
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?
): MethodVisitor? {
var visitedMember: Member? = null
val clazz = currentClass!!
val member = Member(
access = access,
className = clazz.name,
memberName = name,
signature = desc,
genericsDetails = signature ?: "",
exceptions = exceptions?.toMutableSet() ?: mutableSetOf()
)
currentMember = member
sourceLocation = sourceLocation.copy(
memberName = name,
signature = desc,
lineNumber = 0
)
val processMember = captureExceptions {
visitedMember = visitMethod(clazz, member)
}
configuration.memberModule.addToClass(clazz, visitedMember ?: member)
return if (processMember) {
val derivedMember = visitedMember ?: member
super.visitMethod(
derivedMember.access,
derivedMember.memberName,
derivedMember.signature,
signature,
derivedMember.exceptions.toTypedArray()
)?.let { targetVisitor ->
MethodVisitorImpl(targetVisitor, derivedMember)
}
} else {
null
}
}
/**
* Extract information about the traversed field.
*/
override fun visitField(
access: Int, name: String, desc: String, signature: String?, value: Any?
): FieldVisitor? {
var visitedMember: Member? = null
val clazz = currentClass!!
val member = Member(
access = access,
className = clazz.name,
memberName = name,
signature = desc,
genericsDetails = "",
value = value
)
currentMember = member
sourceLocation = sourceLocation.copy(
memberName = name,
signature = desc,
lineNumber = 0
)
val processMember = captureExceptions {
visitedMember = visitField(clazz, member)
}
configuration.memberModule.addToClass(clazz, visitedMember ?: member)
return if (processMember) {
val derivedMember = visitedMember ?: member
super.visitField(
derivedMember.access,
derivedMember.memberName,
derivedMember.signature,
signature,
derivedMember.value
)?.let { targetVisitor ->
FieldVisitorImpl(targetVisitor)
}
} else {
null
}
}
}
/**
* Visitor used to traverse and analyze a method.
*/
private inner class MethodVisitorImpl(
targetVisitor: MethodVisitor,
private val method: Member
) : MethodVisitor(API_VERSION, targetVisitor) {
/**
* Record line number of current instruction.
*/
override fun visitLineNumber(line: Int, start: Label?) {
sourceLocation = sourceLocation.copy(lineNumber = line)
super.visitLineNumber(line, start)
}
/**
* Extract information about provided label.
*/
override fun visitLabel(label: Label) {
visit(CodeLabel(label), defaultFirst = true) {
super.visitLabel(label)
}
}
/**
* Extract information about provided annotations.
*/
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
visitMemberAnnotation(desc)
return super.visitAnnotation(desc, visible)
}
/**
* Write any new method body code, assuming the definition providers
* have provided any. This handler will not be visited if this method
* has no existing code.
*/
override fun visitCode() {
tryReplaceMethodBody()
visit(MethodEntry(method)) {
super.visitCode()
}
}
/**
* Extract information about provided field access instruction.
*/
override fun visitFieldInsn(opcode: Int, owner: String, name: String, desc: String) {
recordMemberReference(owner, name, desc)
visit(MemberAccessInstruction(opcode, owner, name, desc, isMethod = false)) {
super.visitFieldInsn(opcode, owner, name, desc)
}
}
/**
* Extract information about provided method invocation instruction.
*/
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
recordMemberReference(owner, name, desc)
visit(MemberAccessInstruction(opcode, owner, name, desc, itf, isMethod = true)) {
super.visitMethodInsn(opcode, owner, name, desc, itf)
}
}
/**
* Extract information about provided dynamic invocation instruction.
*/
override fun visitInvokeDynamicInsn(name: String, desc: String, bsm: Handle?, vararg bsmArgs: Any?) {
val module = configuration.memberModule
visit(DynamicInvocationInstruction(
name, desc, module.numberOfArguments(desc), module.returnsValueOrReference(desc)
)) {
super.visitInvokeDynamicInsn(name, desc, bsm, *bsmArgs)
}
}
/**
* Extract information about provided jump instruction.
*/
override fun visitJumpInsn(opcode: Int, label: Label) {
visit(BranchInstruction(opcode, label)) {
super.visitJumpInsn(opcode, label)
}
}
/**
* Extract information about provided instruction (general instruction with no operands).
*/
override fun visitInsn(opcode: Int) {
visit(Instruction(opcode)) {
super.visitInsn(opcode)
}
}
/**
* Extract information about provided instruction (general instruction with one operand).
*/
override fun visitIntInsn(opcode: Int, operand: Int) {
visit(IntegerInstruction(opcode, operand)) {
super.visitIntInsn(opcode, operand)
}
}
/**
* Extract information about provided type instruction (e.g., [Opcodes.NEW], [Opcodes.ANEWARRAY],
* [Opcodes.INSTANCEOF] and [Opcodes.CHECKCAST]).
*/
override fun visitTypeInsn(opcode: Int, type: String) {
recordTypeReference(type)
visit(TypeInstruction(opcode, type)) {
try {
super.visitTypeInsn(opcode, type)
} catch (exception: IllegalArgumentException) {
throw IllegalArgumentException("Invalid name used in type instruction; $type", exception)
}
}
}
/**
* Extract information about provided try-catch/finally block.
*/
override fun visitTryCatchBlock(start: Label, end: Label, handler: Label, type: String?) {
val block = if (type != null) {
TryCatchBlock(type, handler)
} else {
TryFinallyBlock(handler)
}
visit(block) {
super.visitTryCatchBlock(start, end, handler, type)
}
}
/**
* Extract information about provided table switch instruction.
*/
override fun visitTableSwitchInsn(min: Int, max: Int, dflt: Label, vararg labels: Label) {
visit(TableSwitchInstruction(min, max, dflt, labels.toList())) {
super.visitTableSwitchInsn(min, max, dflt, *labels)
}
}
/**
* Extract information about provided increment instruction.
*/
override fun visitIincInsn(`var`: Int, increment: Int) {
visit(IntegerInstruction(Opcodes.IINC, increment)) {
super.visitIincInsn(`var`, increment)
}
}
/**
* Transform values loaded from the constants pool.
*/
override fun visitLdcInsn(value: Any) {
visit(ConstantInstruction(value), defaultFirst = true) {
super.visitLdcInsn(value)
}
}
/**
* Finish visiting this method, writing any new method body byte-code
* if we haven't written it already. This would (presumably) only happen
* for methods that previously had no body, e.g. native methods.
*/
override fun visitEnd() {
tryReplaceMethodBody()
super.visitEnd()
}
private fun tryReplaceMethodBody() {
if (method.body.isNotEmpty() && (mv != null)) {
EmitterModule(mv).apply {
for (body in method.body) {
body(this)
}
}
mv.visitMaxs(-1, -1)
mv.visitEnd()
mv = null
}
}
/**
* Helper function used to streamline the access to an instruction and to catch any related processing errors.
*/
private inline fun visit(instruction: Instruction, defaultFirst: Boolean = false, defaultAction: () -> Unit) {
val emitterModule = EmitterModule(mv ?: StubMethodVisitor())
if (defaultFirst) {
defaultAction()
}
val success = captureExceptions {
visitInstruction(currentMember!!, emitterModule, instruction)
}
if (!defaultFirst) {
if (success && emitterModule.emitDefaultInstruction) {
defaultAction()
}
}
}
}
/**
* Visitor used to traverse and analyze a field.
*/
private inner class FieldVisitorImpl(
targetVisitor: FieldVisitor
) : FieldVisitor(API_VERSION, targetVisitor) {
/**
* Extract information about provided annotations.
*/
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
visitMemberAnnotation(desc)
return super.visitAnnotation(desc, visible)
}
}
private inner class StubMethodVisitor : MethodVisitor(API_VERSION, null)
companion object {
/**
* The API version of ASM.
*/
const val API_VERSION: Int = Opcodes.ASM6
}
}

View File

@ -1,156 +0,0 @@
package net.corda.djvm.analysis
import net.corda.djvm.code.asPackagePath
import net.corda.djvm.code.asResourcePath
/**
* Functionality for resolving the class name of a sandboxable class.
*
* The resolution of a class name entails determining whether the class can be instrumented or not. This means that the
* following criteria need to be satisfied:
* - The class do not reside in the "java/lang" package.
* - The class has not been explicitly pinned.
* - The class does not already reside in the top-level package named [sandboxPrefix].
*
* If these criteria have been satisfied, the fully-qualified class name will be derived by prepending [sandboxPrefix]
* to it. Note that [ClassLoader] will not allow defining a class in a package whose fully-qualified class name starts
* with "java/". That will result in the class loader throwing [SecurityException]. Also, some values map onto types
* defined in "java/lang/", e.g., [Integer] and [String]. These cannot be trivially moved into a different package due
* to the internal mechanisms of the JVM.
*
* @property pinnedClasses Classes that have already been declared in the sandbox namespace and that should be made
* available inside the sandboxed environment.
* @property whitelist The set of classes in the Java runtime libraries that have been whitelisted and that should be
* left alone.
* @property sandboxPrefix The package name prefix to use for classes loaded into a sandbox.
*/
class ClassResolver(
private val pinnedClasses: Set<String>,
private val templateClasses: Set<String>,
private val whitelist: Whitelist,
private val sandboxPrefix: String
) {
/**
* Resolve the class name from a fully qualified name.
*/
fun resolve(name: String): String {
return when {
name.startsWith('[') && name.endsWith(';') -> {
complexArrayTypeRegex.replace(name) {
"${it.groupValues[1]}L${resolveName(it.groupValues[2])};"
}
}
name.startsWith('[') && !name.endsWith(';') -> name
else -> resolveName(name)
}
}
/**
* Resolve the class name from a fully qualified normalized name.
*/
fun resolveNormalized(name: String): String {
return resolve(name.asResourcePath).asPackagePath
}
/**
* Derive descriptor by resolving all referenced class names.
*/
fun resolveDescriptor(descriptor: String): String {
val outputDescriptor = StringBuilder()
var longName = StringBuilder()
var isProcessingLongName = false
loop@ for (char in descriptor) {
when {
char != ';' && isProcessingLongName -> {
longName.append(char)
continue@loop
}
char == 'L' -> {
isProcessingLongName = true
longName = StringBuilder()
}
char == ';' -> {
outputDescriptor.append(resolve(longName.toString()))
isProcessingLongName = false
}
}
outputDescriptor.append(char)
}
return outputDescriptor.toString()
}
/**
* Reverse the resolution of a class name.
*/
fun reverse(resolvedClassName: String): String {
return if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) {
resolvedClassName
} else {
removeSandboxPrefix(resolvedClassName)
}
}
/**
* Reverse the resolution of a class name from a fully qualified normalized name.
*/
fun reverseNormalized(className: String): String {
return reverse(className.asResourcePath).asPackagePath
}
/**
* Generates the equivalent class name outside the sandbox from a fully qualified normalized name.
*/
fun toSourceNormalized(className: String): String {
return toSource(className.asResourcePath).asPackagePath
}
/**
* Resolve sandboxed class name from a fully qualified name.
*/
private fun resolveName(name: String): String {
return if (isPinnedOrWhitelistedClass(name) || name in templateClasses) {
name
} else {
"$sandboxPrefix$name"
}
}
/**
* Maps a class name to its equivalent class outside the sandbox.
* Needed by [net.corda.djvm.source.SourceClassLoader].
*/
private fun toSource(className: String): String {
return if (className in pinnedClasses) {
className
} else {
removeSandboxPrefix(className)
}
}
private fun removeSandboxPrefix(className: String): String {
if (className.startsWith(sandboxPrefix)) {
val nameWithoutPrefix = className.drop(sandboxPrefix.length)
if (resolve(nameWithoutPrefix) == className) {
return nameWithoutPrefix
}
}
return className
}
/**
* Check if class is whitelisted or pinned.
*/
private fun isPinnedOrWhitelistedClass(name: String): Boolean {
return whitelist.matches(name) ||
name in pinnedClasses ||
sandboxRegex.matches(name)
}
private val sandboxRegex = "^$sandboxPrefix.*\$".toRegex()
companion object {
private val complexArrayTypeRegex = "^(\\[+)L(.*);\$".toRegex()
}
}

View File

@ -1,41 +0,0 @@
package net.corda.djvm.analysis
import org.objectweb.asm.Type
class ExceptionResolver(
private val jvmExceptionClasses: Set<String>,
private val pinnedClasses: Set<String>,
private val sandboxPrefix: String
) {
companion object {
private const val DJVM_EXCEPTION_NAME = "\$1DJVM"
fun isDJVMException(className: String): Boolean = className.endsWith(DJVM_EXCEPTION_NAME)
fun getDJVMException(className: String): String = className + DJVM_EXCEPTION_NAME
fun getDJVMExceptionOwner(className: String): String = className.dropLast(DJVM_EXCEPTION_NAME.length)
}
fun getThrowableName(clazz: Class<*>): String {
return getDJVMException(Type.getInternalName(clazz))
}
fun getThrowableSuperName(clazz: Class<*>): String {
return getThrowableOwnerName(Type.getInternalName(clazz.superclass))
}
fun getThrowableOwnerName(className: String): String {
return if (className in jvmExceptionClasses) {
className.unsandboxed
} else if (className in pinnedClasses) {
className
} else {
getDJVMException(className)
}
}
private val String.unsandboxed: String get() = if (startsWith(sandboxPrefix)) {
drop(sandboxPrefix.length)
} else {
this
}
}

View File

@ -1,89 +0,0 @@
package net.corda.djvm.analysis
import net.corda.djvm.formatting.MemberFormatter
import net.corda.djvm.references.MemberInformation
/**
* Representation of the source location of a class, member or instruction.
*
* @property className The name of the class.
* @property sourceFile The file containing the source of the compiled class.
* @property memberName The name of the field or method.
* @property signature The signature of the field or method.
* @property lineNumber The index of the line from which the instruction was compiled.
*/
data class SourceLocation(
override val className: String = "",
val sourceFile: String = "",
override val memberName: String = "",
override val signature: String = "",
val lineNumber: Int = 0
) : MemberInformation {
/**
* Check whether or not information about the source file is available.
*/
val hasSourceFile: Boolean
get() = sourceFile.isNotBlank()
/**
* Check whether or not information about the line number is available.
*/
val hasLineNumber: Boolean
get() = lineNumber != 0
/**
* Get a string representation of the source location.
*/
override fun toString(): String {
return StringBuilder().apply {
append(className.removePrefix("sandbox/"))
if (memberName.isNotBlank()) {
append(".$memberName")
if (memberFormatter.isMethod(signature)) {
append("(${memberFormatter.format(signature)})")
}
}
}.toString()
}
/**
* Get a formatted string representation of the source location.
*/
fun format(): String {
if (className.isBlank()) {
return ""
}
return StringBuilder().apply {
append("@|blue ")
append(if (hasSourceFile) {
sourceFile
} else {
className
}.removePrefix("sandbox/"))
append("|@")
if (hasLineNumber) {
append(" on @|yellow line $lineNumber|@")
}
if (memberName.isNotBlank()) {
append(" in @|green ")
if (hasSourceFile) {
append("${memberFormatter.getShortClassName(className)}.$memberName")
} else {
append(memberName)
}
if (memberFormatter.isMethod(signature)) {
append("(${memberFormatter.format(signature)})")
}
append("|@")
}
}.toString()
}
private companion object {
private val memberFormatter = MemberFormatter()
}
}

View File

@ -1,192 +0,0 @@
package net.corda.djvm.analysis
import java.io.FileNotFoundException
import java.io.InputStream
import java.io.PushbackInputStream
import java.nio.file.Files
import java.nio.file.Path
import java.util.zip.GZIPInputStream
/**
* Representation of a whitelist deciding what classes, interfaces and members are permissible and consequently can be
* referenced from sandboxed code.
*
* @property namespace If provided, this parameter bounds the namespace of the whitelist.
* @property entries A set of regular expressions used to determine whether a name is covered by the whitelist or not.
* @property textEntries A set of textual entries used to determine whether a name is covered by the whitelist or not.
*/
open class Whitelist private constructor(
private val namespace: Whitelist? = null,
private val entries: Set<Regex>,
private val textEntries: Set<String>
) {
/**
* Set of seen names that matched with the whitelist.
*/
private val seenNames = mutableSetOf<String>()
/**
* Check if name falls within the namespace of the whitelist.
*/
fun inNamespace(name: String): Boolean {
return namespace != null && namespace.matches(name)
}
/**
* Check if a name is covered by the whitelist.
*/
fun matches(name: String): Boolean {
if (name in seenNames) {
return true
}
return when {
name in textEntries -> {
seenNames.add(name)
true
}
entries.any { it.matches(name) } -> {
seenNames.add(name)
true
}
else -> false
}
}
/**
* Combine two whitelists into one.
*/
operator fun plus(whitelist: Whitelist): Whitelist {
val entries = entries + whitelist.entries
val textEntries = textEntries + whitelist.textEntries
return when {
namespace != null && whitelist.namespace != null ->
Whitelist(namespace + whitelist.namespace, entries, textEntries)
namespace != null ->
Whitelist(namespace, entries, textEntries)
whitelist.namespace != null ->
Whitelist(whitelist.namespace, entries, textEntries)
else ->
Whitelist(null, entries, textEntries)
}
}
/**
* Get a derived whitelist by adding a set of additional entries.
*/
operator fun plus(additionalEntries: Set<Regex>): Whitelist {
return plus(Whitelist(null, additionalEntries, emptySet()))
}
/**
* Get a derived whitelist by adding an additional entry.
*/
operator fun plus(additionalEntry: Regex): Whitelist {
return plus(setOf(additionalEntry))
}
/**
* Enumerate all the entries of the whitelist.
*/
val items: Set<String>
get() = textEntries + entries.map(Regex::pattern)
companion object {
private val everythingRegex = setOf(".*".toRegex())
private val minimumSet = setOf(
"^java/lang/Class(\\..*)?\$".toRegex(),
"^java/lang/ClassLoader(\\..*)?\$".toRegex(),
"^java/lang/Cloneable(\\..*)?\$".toRegex(),
"^java/lang/Object(\\..*)?\$".toRegex(),
"^java/lang/Override(\\..*)?\$".toRegex(),
"^java/lang/Void\$".toRegex(),
"^java/lang/invoke/LambdaMetafactory\$".toRegex(),
"^java/lang/invoke/MethodHandles(\\\$.*)?\$".toRegex(),
"^java/lang/reflect/Array(\\..*)?\$".toRegex(),
"^java/io/Serializable\$".toRegex()
)
/**
* Empty whitelist.
*/
val EMPTY: Whitelist = Whitelist(null, emptySet(), emptySet())
/**
* The minimum set of classes that needs to be pinned from standard Java libraries.
*/
val MINIMAL: Whitelist = Whitelist(Whitelist(null, minimumSet, emptySet()), minimumSet, emptySet())
/**
* Whitelist everything.
*/
val EVERYTHING: Whitelist = Whitelist(
Whitelist(null, everythingRegex, emptySet()),
everythingRegex,
emptySet()
)
/**
* Load a whitelist from a resource stream.
*/
fun fromResource(resourceName: String): Whitelist {
val inputStream = Whitelist::class.java.getResourceAsStream("/$resourceName")
?: throw FileNotFoundException("Cannot find resource \"$resourceName\"")
return fromStream(inputStream)
}
/**
* Load a whitelist from a file.
*/
fun fromFile(file: Path): Whitelist {
return Files.newInputStream(file).use(this::fromStream)
}
/**
* Load a whitelist from a GZIP'ed or raw input stream.
*/
fun fromStream(inputStream: InputStream): Whitelist {
val namespaceEntries = mutableSetOf<Regex>()
val entries = mutableSetOf<String>()
decompressStream(inputStream).bufferedReader().use {
var isCollectingFilterEntries = false
for (line in it.lines().filter(String::isNotBlank)) {
when {
line.trim() == SECTION_SEPARATOR -> {
isCollectingFilterEntries = true
}
isCollectingFilterEntries -> entries.add(line)
else -> namespaceEntries.add(Regex(line))
}
}
}
val namespace = if (namespaceEntries.isNotEmpty()) {
Whitelist(null, namespaceEntries, emptySet())
} else {
null
}
return Whitelist(namespace = namespace, entries = emptySet(), textEntries = entries)
}
/**
* Decompress stream if GZIP'ed, otherwise, use the raw stream.
*/
private fun decompressStream(inputStream: InputStream): InputStream {
val rawStream = PushbackInputStream(inputStream, 2)
val signature = ByteArray(2)
val length = rawStream.read(signature)
rawStream.unread(signature, 0, length)
return if (signature[0] == GZIP_MAGIC_FIRST_BYTE && signature[1] == GZIP_MAGIC_SECOND_BYTE) {
GZIPInputStream(rawStream)
} else {
rawStream
}
}
private const val SECTION_SEPARATOR = "---"
private const val GZIP_MAGIC_FIRST_BYTE = GZIPInputStream.GZIP_MAGIC.toByte()
private const val GZIP_MAGIC_SECOND_BYTE = (GZIPInputStream.GZIP_MAGIC shr 8).toByte()
}
}

View File

@ -1,22 +0,0 @@
package net.corda.djvm.code
import net.corda.djvm.analysis.AnalysisRuntimeContext
import net.corda.djvm.references.ClassRepresentation
/**
* A class definition provider is a hook for [ClassMutator], from where one can modify the name and meta-data of
* processed classes.
*/
interface ClassDefinitionProvider : DefinitionProvider {
/**
* Hook for providing modifications to a class definition.
*
* @param context The context in which the hook is called.
* @param clazz The original class definition.
*
* @return The updated class definition, or [clazz] if no changes are desired.
*/
fun define(context: AnalysisRuntimeContext, clazz: ClassRepresentation): ClassRepresentation
}

View File

@ -1,148 +0,0 @@
package net.corda.djvm.code
import net.corda.djvm.analysis.AnalysisConfiguration
import net.corda.djvm.analysis.ClassAndMemberVisitor
import net.corda.djvm.code.instructions.MethodEntry
import net.corda.djvm.references.ClassRepresentation
import net.corda.djvm.references.Member
import net.corda.djvm.references.MethodBody
import net.corda.djvm.utilities.Processor
import net.corda.djvm.utilities.loggerFor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.Opcodes.*
/**
* Helper class for applying a set of definition providers and emitters to a class or set of classes.
*
* @param classVisitor Class visitor to use when traversing the structure of classes.
* @property configuration The configuration to use for class analysis.
* @property definitionProviders A set of providers used to update the name or meta-data of classes and members.
* @param emitters A set of code emitters used to modify and instrument method bodies.
*/
class ClassMutator(
classVisitor: ClassVisitor,
private val configuration: AnalysisConfiguration,
private val definitionProviders: List<DefinitionProvider> = emptyList(),
emitters: List<Emitter> = emptyList()
) : ClassAndMemberVisitor(configuration, classVisitor) {
/**
* Internal [Emitter] to add static field initializers to
* any class constructor method.
*/
private inner class PrependClassInitializer : Emitter {
override fun emit(context: EmitterContext, instruction: Instruction) = context.emit {
if (instruction is MethodEntry
&& instruction.method.memberName == "<clinit>" && instruction.method.signature == "()V"
&& initializers.isNotEmpty()) {
writeByteCode(initializers)
initializers.clear()
}
}
}
/*
* Some emitters must be executed before others. E.g. we need to apply
* the tracing emitters before the non-tracing ones.
*/
private val emitters: List<Emitter> = (emitters + PrependClassInitializer()).sortedBy(Emitter::priority)
private val initializers = mutableListOf<MethodBody>()
/**
* Tracks whether any modifications have been applied to any of the processed class(es) and pertinent members.
*/
var hasBeenModified: Boolean = false
private set
/**
* Apply definition providers to a class. This can be used to update the name or definition (pertinent meta-data)
* of the class itself.
*/
override fun visitClass(clazz: ClassRepresentation): ClassRepresentation {
var resultingClass = clazz
Processor.processEntriesOfType<ClassDefinitionProvider>(definitionProviders, analysisContext.messages) {
resultingClass = it.define(currentAnalysisContext(), resultingClass)
}
if (clazz != resultingClass) {
logger.trace("Type has been mutated {}", clazz)
hasBeenModified = true
}
return super.visitClass(resultingClass)
}
/**
* If we have some static fields to initialise, and haven't already added them
* to an existing class initialiser block then we need to create one.
*/
override fun visitClassEnd(classVisitor: ClassVisitor, clazz: ClassRepresentation) {
tryWriteClassInitializer(classVisitor)
super.visitClassEnd(classVisitor, clazz)
}
private fun tryWriteClassInitializer(classVisitor: ClassVisitor) {
if (initializers.isNotEmpty()) {
classVisitor.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null)?.also { mv ->
mv.visitCode()
EmitterModule(mv).writeByteCode(initializers)
mv.visitInsn(RETURN)
mv.visitMaxs(-1, -1)
mv.visitEnd()
}
initializers.clear()
hasBeenModified = true
}
}
/**
* Apply definition providers to a method. This can be used to update the name or definition (pertinent meta-data)
* of a class member.
*/
override fun visitMethod(clazz: ClassRepresentation, method: Member): Member {
var resultingMethod = method
Processor.processEntriesOfType<MemberDefinitionProvider>(definitionProviders, analysisContext.messages) {
resultingMethod = it.define(currentAnalysisContext(), resultingMethod)
}
if (method != resultingMethod) {
logger.trace("Method has been mutated {}", method)
hasBeenModified = true
}
return super.visitMethod(clazz, resultingMethod)
}
/**
* Apply definition providers to a field. This can be used to update the name or definition (pertinent meta-data)
* of a class member.
*/
override fun visitField(clazz: ClassRepresentation, field: Member): Member {
var resultingField = field
Processor.processEntriesOfType<MemberDefinitionProvider>(definitionProviders, analysisContext.messages) {
resultingField = it.define(currentAnalysisContext(), resultingField)
}
if (field != resultingField) {
logger.trace("Field has been mutated {}", field)
initializers += resultingField.body
hasBeenModified = true
}
return super.visitField(clazz, resultingField)
}
/**
* Apply emitters to an instruction. This can be used to instrument a part of the code block, change behaviour of
* an existing instruction, or strip it out completely.
*/
override fun visitInstruction(method: Member, emitter: EmitterModule, instruction: Instruction) {
val context = EmitterContext(currentAnalysisContext(), configuration, emitter)
Processor.processEntriesOfType<Emitter>(emitters, analysisContext.messages) {
it.emit(context, instruction)
}
if (!emitter.emitDefaultInstruction || emitter.hasEmittedCustomCode) {
hasBeenModified = true
}
super.visitInstruction(method, emitter, instruction)
}
private companion object {
private val logger = loggerFor<ClassMutator>()
}
}

View File

@ -1,7 +0,0 @@
package net.corda.djvm.code
/**
* A definition provider is a hook for [ClassMutator], from where one can modify the name and meta-data of processed
* classes and class members.
*/
interface DefinitionProvider

View File

@ -1,27 +0,0 @@
package net.corda.djvm.code
/**
* An emitter is a hook for [ClassMutator], from where one can modify the byte code of a class method.
*/
interface Emitter {
/**
* Hook for providing modifications to an instruction in a method body. One can also prepend and append instructions
* by using the [EmitterContext], and skip the default instruction altogether by invoking
* [EmitterModule.preventDefault] from within [EmitterContext.emit].
*
* @param context The context from which the emitter is invoked. By calling [EmitterContext.emit], one gets access
* to an instance of [EmitterModule] from within the supplied closure. From there, one can emit new instructions and
* intercept the original instruction (for instance, modify or delete the instruction).
* @param instruction The instruction currently being processed.
*/
fun emit(context: EmitterContext, instruction: Instruction)
/**
* Determines the order in which emitters are executed within the sandbox.
*/
@JvmDefault
val priority: Int
get() = EMIT_DEFAULT
}

View File

@ -1,76 +0,0 @@
package net.corda.djvm.code
import net.corda.djvm.analysis.AnalysisConfiguration
import net.corda.djvm.analysis.AnalysisRuntimeContext
import net.corda.djvm.analysis.SourceLocation
import net.corda.djvm.analysis.Whitelist
import net.corda.djvm.references.ClassRepresentation
import net.corda.djvm.references.ClassModule
import net.corda.djvm.references.Member
import net.corda.djvm.references.MemberModule
/**
* The context in which an emitter is invoked.
*
* @property analysisContext The context in which a class and its members are processed.
* @property configuration The configuration to used for the analysis.
* @property emitterModule A module providing code generation functionality that can be used from within an emitter.
*/
@Suppress("unused")
open class EmitterContext(
private val analysisContext: AnalysisRuntimeContext,
private val configuration: AnalysisConfiguration,
val emitterModule: EmitterModule
) {
/**
* The class currently being analysed.
*/
val clazz: ClassRepresentation
get() = analysisContext.clazz
/**
* The member currently being analysed, if any.
*/
val member: Member?
get() = analysisContext.member
/**
* The current source location.
*/
val location: SourceLocation
get() = analysisContext.location
/**
* The configured whitelist.
*/
val whitelist: Whitelist
get() = analysisContext.configuration.whitelist
/**
* Utilities for dealing with classes.
*/
val classModule: ClassModule
get() = analysisContext.configuration.classModule
/**
* Utilities for dealing with members.
*/
val memberModule: MemberModule
get() = analysisContext.configuration.memberModule
/**
* Resolve the sandboxed name of a class or interface.
*/
fun resolve(typeName: String): String {
return configuration.classResolver.resolve(typeName)
}
/**
* Set up and execute an emitter block for a particular member.
*/
inline fun emit(action: EmitterModule.() -> Unit) {
action(emitterModule)
}
}

View File

@ -1,228 +0,0 @@
package net.corda.djvm.code
import net.corda.djvm.references.MethodBody
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type
import sandbox.net.corda.djvm.costing.RuntimeCostAccounter
/**
* Helper functions for emitting code to a method body.
*
* @property methodVisitor The underlying visitor which controls all the byte code for the current method.
*/
@Suppress("unused")
class EmitterModule(
private val methodVisitor: MethodVisitor
) {
/**
* Indicates whether the default instruction in the currently processed block is to be emitted or not.
*/
var emitDefaultInstruction: Boolean = true
private set
/**
* Indicates whether any custom code has been emitted in the applicable context.
*/
var hasEmittedCustomCode: Boolean = false
private set
/**
* Emit instruction for creating a new object of type [typeName].
*/
fun new(typeName: String, opcode: Int = NEW) {
hasEmittedCustomCode = true
methodVisitor.visitTypeInsn(opcode, typeName)
}
/**
* Emit instruction for creating a new object of type [T].
*/
inline fun <reified T> new() {
new(Type.getInternalName(T::class.java))
}
/**
* Emit instruction for loading a constant onto the stack.
*/
fun loadConstant(constant: Any) {
hasEmittedCustomCode = true
methodVisitor.visitLdcInsn(constant)
}
/**
* Emit instruction for invoking a static method.
*/
fun invokeStatic(owner: String, name: String, descriptor: String, isInterface: Boolean = false) {
hasEmittedCustomCode = true
methodVisitor.visitMethodInsn(INVOKESTATIC, owner, name, descriptor, isInterface)
}
/**
* Emit instruction for invoking a virtual method.
*/
fun invokeVirtual(owner: String, name: String, descriptor: String, isInterface: Boolean = false) {
hasEmittedCustomCode = true
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, owner, name, descriptor, isInterface)
}
/**
* Emit instruction for invoking a special method, e.g. a constructor or a method on a super-type.
*/
fun invokeSpecial(owner: String, name: String, descriptor: String, isInterface: Boolean = false) {
hasEmittedCustomCode = true
methodVisitor.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface)
}
/**
* Emit instruction for invoking a special method on class [T], e.g. a constructor or a method on a super-type.
*/
inline fun <reified T> invokeSpecial(name: String, descriptor: String, isInterface: Boolean = false) {
invokeSpecial(Type.getInternalName(T::class.java), name, descriptor, isInterface)
}
fun invokeInterface(owner: String, name: String, descriptor: String) {
methodVisitor.visitMethodInsn(INVOKEINTERFACE, owner, name, descriptor, true)
hasEmittedCustomCode = true
}
/**
* Emit instruction for storing a value into a static field.
*/
fun putStatic(owner: String, name: String, descriptor: String) {
methodVisitor.visitFieldInsn(PUTSTATIC, owner, name, descriptor)
hasEmittedCustomCode = true
}
/**
* Emit instruction for popping one element off the stack.
*/
fun pop() {
hasEmittedCustomCode = true
methodVisitor.visitInsn(POP)
}
/**
* Emit instruction for duplicating the top of the stack.
*/
fun duplicate() {
hasEmittedCustomCode = true
methodVisitor.visitInsn(DUP)
}
/**
* Emit instruction for pushing an object reference
* from a register onto the stack.
*/
fun pushObject(regNum: Int) {
methodVisitor.visitVarInsn(ALOAD, regNum)
hasEmittedCustomCode = true
}
/**
* Emit instruction for pushing an integer value
* from a register onto the stack.
*/
fun pushInteger(regNum: Int) {
methodVisitor.visitVarInsn(ILOAD, regNum)
hasEmittedCustomCode = true
}
/**
* Emit instructions to rearrange the stack as follows:
* [W1] [W3]
* [W2] -> [W1]
* [w3] [W2]
*/
fun raiseThirdWordToTop() {
methodVisitor.visitInsn(DUP2_X1)
methodVisitor.visitInsn(POP2)
hasEmittedCustomCode = true
}
/**
* Emit instructions to rearrange the stack as follows:
* [W1] [W2]
* [W2] -> [W3]
* [W3] [W1]
*/
fun sinkTopToThirdWord() {
methodVisitor.visitInsn(DUP_X2)
methodVisitor.visitInsn(POP)
hasEmittedCustomCode = true
}
/**
* Emit a sequence of instructions for instantiating and throwing an exception based on the provided message.
*/
fun <T : Throwable> throwException(exceptionType: Class<T>, message: String) {
val exceptionName = Type.getInternalName(exceptionType)
new(exceptionName)
methodVisitor.visitInsn(DUP)
methodVisitor.visitLdcInsn(message)
invokeSpecial(exceptionName, "<init>", "(Ljava/lang/String;)V")
methodVisitor.visitInsn(ATHROW)
}
inline fun <reified T : Throwable> throwException(message: String) = throwException(T::class.java, message)
/**
* Attempt to cast the object on the top of the stack to the given class.
*/
fun castObjectTo(className: String) {
methodVisitor.visitTypeInsn(CHECKCAST, className)
hasEmittedCustomCode = true
}
/**
* Emit instruction for returning from "void" method.
*/
fun returnVoid() {
methodVisitor.visitInsn(RETURN)
hasEmittedCustomCode = true
}
/**
* Emit instruction for a function that returns an object reference.
*/
fun returnObject() {
methodVisitor.visitInsn(ARETURN)
hasEmittedCustomCode = true
}
/**
* Emit instructions for a new line number.
*/
fun lineNumber(line: Int) {
val label = Label()
methodVisitor.visitLabel(label)
methodVisitor.visitLineNumber(line, label)
hasEmittedCustomCode = true
}
/**
* Write the bytecode from these [MethodBody] objects as provided.
*/
fun writeByteCode(bodies: Iterable<MethodBody>) {
for (body in bodies) {
body(this)
}
}
/**
* Tell the code writer not to emit the default instruction.
*/
fun preventDefault() {
emitDefaultInstruction = false
}
/**
* Emit instruction for invoking a method on the static runtime cost accounting and instrumentation object.
*/
fun invokeInstrumenter(methodName: String, methodSignature: String) {
invokeStatic(RuntimeCostAccounter.TYPE_NAME, methodName, methodSignature)
}
}

View File

@ -1,23 +0,0 @@
package net.corda.djvm.code
import org.objectweb.asm.Opcodes
/**
* Byte code instruction.
*
* @property operation The operation code, enumerated in [Opcodes].
*/
open class Instruction(
val operation: Int
) {
companion object {
/**
* Byte code for the breakpoint operation.
*/
const val OP_BREAKPOINT: Int = 202
}
}

View File

@ -1,22 +0,0 @@
package net.corda.djvm.code
import net.corda.djvm.analysis.AnalysisRuntimeContext
import net.corda.djvm.references.Member
/**
* A member definition provider is a hook for [ClassMutator], from where one can modify the name and meta-data of
* processed class members.
*/
interface MemberDefinitionProvider : DefinitionProvider {
/**
* Hook for providing modifications to a member definition.
*
* @param context The context in which the hook is called.
* @param member The original member definition.
*
* @return The updated member definition, or [member] if no changes are desired.
*/
fun define(context: AnalysisRuntimeContext, member: Member): Member
}

View File

@ -1,28 +0,0 @@
@file:JvmName("Types")
package net.corda.djvm.code
import org.objectweb.asm.Type
import sandbox.java.lang.DJVMException
import sandbox.net.corda.djvm.costing.ThresholdViolationError
import sandbox.net.corda.djvm.rules.RuleViolationError
/**
* These are the priorities for executing [Emitter] instances.
* Tracing emitters are executed first.
*/
const val EMIT_TRACING: Int = 0
const val EMIT_TRAPPING_EXCEPTIONS: Int = EMIT_TRACING + 1
const val EMIT_HANDLING_EXCEPTIONS: Int = EMIT_TRAPPING_EXCEPTIONS + 1
const val EMIT_DEFAULT: Int = 10
val ruleViolationError: String = Type.getInternalName(RuleViolationError::class.java)
val thresholdViolationError: String = Type.getInternalName(ThresholdViolationError::class.java)
val djvmException: String = Type.getInternalName(DJVMException::class.java)
/**
* Local extension method for normalizing a class name.
*/
val String.asPackagePath: String get() = this.replace('/', '.')
val String.asResourcePath: String get() = this.replace('.', '/')
val String.emptyAsNull: String? get() = if (isEmpty()) null else this

View File

@ -1,15 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
import org.objectweb.asm.Label
/**
* Branch instruction.
*
* @property label The label of the target.
*/
@Suppress("MemberVisibilityCanBePrivate")
class BranchInstruction(
operation: Int,
val label: Label
) : Instruction(operation)

View File

@ -1,12 +0,0 @@
package net.corda.djvm.code.instructions
import org.objectweb.asm.Label
/**
* Label of a code block.
*
* @property label The label for the given code block.
*/
class CodeLabel(
val label: Label
) : NoOperationInstruction()

View File

@ -1,6 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
import org.objectweb.asm.Opcodes
class ConstantInstruction(val value: Any) : Instruction(Opcodes.LDC)

View File

@ -1,20 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
import org.objectweb.asm.Opcodes
/**
* Dynamic invocation instruction.
*
* @property memberName The name of the method to invoke.
* @property signature The function signature of the method being invoked.
* @property numberOfArguments The number of arguments to pass to the target.
* @property returnsValueOrReference False if the target returns `void`, or true if it returns a value or a reference.
*/
@Suppress("MemberVisibilityCanBePrivate")
class DynamicInvocationInstruction(
val memberName: String,
val signature: String,
val numberOfArguments: Int,
val returnsValueOrReference: Boolean
) : Instruction(Opcodes.INVOKEDYNAMIC)

View File

@ -1,13 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
/**
* Instruction with a single, constant integer operand.
*
* @property operand The integer operand.
*/
class IntegerInstruction(
operation: Int,
val operand: Int
) : Instruction(operation)

View File

@ -1,35 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
import net.corda.djvm.references.MemberReference
/**
* Field access and method invocation instruction.
*
* @property owner The class owning the field or method.
* @property memberName The name of the field or the method being accessed.
* @property signature The return type of a field or function signature for a method.
* @property ownerIsInterface If the member is a method, this is true if the owner is an interface.
* @property isMethod Indicates whether the member is a method or a field.
*/
class MemberAccessInstruction(
operation: Int,
val owner: String,
val memberName: String,
val signature: String,
val ownerIsInterface: Boolean = false,
val isMethod: Boolean = false
) : Instruction(operation) {
/**
* The absolute name of the referenced member.
*/
val reference = "$owner.$memberName:$signature"
/**
* Get a member reference representation of the target of the instruction.
*/
val member: MemberReference
get() = MemberReference(owner, memberName, signature)
}

View File

@ -1,9 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.references.Member
/**
* Pseudo-instruction marking the beginning of a method.
* @property method [Member] describing this method.
*/
class MethodEntry(val method: Member): NoOperationInstruction()

View File

@ -1,9 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
import org.objectweb.asm.Opcodes
/**
* Instruction that, surprise surprise (!), does nothing!
*/
open class NoOperationInstruction : Instruction(Opcodes.NOP)

View File

@ -1,22 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
import org.objectweb.asm.Label
import org.objectweb.asm.Opcodes
/**
* Table switch instruction.
*
* @property min The minimum key value.
* @property max The maximum key value.
* @property defaultHandler The label of the default handler block.
* @property handlers The labels of each of the handler blocks, where the label of the handler block for key
* `min + i` is at index `i` in `handlers`.
*/
@Suppress("MemberVisibilityCanBePrivate")
class TableSwitchInstruction(
val min: Int,
val max: Int,
val defaultHandler: Label,
val handlers: List<Label>
) : Instruction(Opcodes.TABLESWITCH)

View File

@ -1,8 +0,0 @@
package net.corda.djvm.code.instructions
import org.objectweb.asm.Label
open class TryBlock(
val handler: Label,
val typeName: String
) : NoOperationInstruction()

View File

@ -1,14 +0,0 @@
package net.corda.djvm.code.instructions
import org.objectweb.asm.Label
/**
* Try-catch block.
*
* @property typeName The type of the exception being caught.
* @property handler The label of the exception handler.
*/
class TryCatchBlock(
typeName: String,
handler: Label
) : TryBlock(handler, typeName)

View File

@ -1,12 +0,0 @@
package net.corda.djvm.code.instructions
import org.objectweb.asm.Label
/**
* Try-finally block.
*
* @property handler The handler for the finally-block.
*/
class TryFinallyBlock(
handler: Label
) : TryBlock(handler, "")

View File

@ -1,13 +0,0 @@
package net.corda.djvm.code.instructions
import net.corda.djvm.code.Instruction
/**
* Object instantiation instruction.
*
* @property typeName The class name of the object being instantiated.
*/
class TypeInstruction(
operation: Int,
val typeName: String
) : Instruction(operation)

View File

@ -1,30 +0,0 @@
package net.corda.djvm.costing
/**
* Cost metric to be used in a sandbox environment. The metric has a threshold and a mechanism for reporting violations.
* The implementation assumes that each metric is tracked on a per-thread basis, i.e., that each sandbox runs on its own
* thread.
*
* @param threshold The threshold for this metric.
* @param errorMessage A delegate for generating an error message based on the thread it was reported from.
*/
class RuntimeCost(
threshold: Long,
errorMessage: (Thread) -> String
) : TypedRuntimeCost<Long>(
0,
{ value: Long -> value > threshold },
errorMessage
) {
/**
* Increment the accumulated cost by an integer.
*/
fun increment(incrementBy: Int) = increment(incrementBy.toLong())
/**
* Increment the accumulated cost by a long integer.
*/
fun increment(incrementBy: Long = 1) = incrementAndCheck { value -> Math.addExact(value, incrementBy) }
}

View File

@ -1,57 +0,0 @@
package net.corda.djvm.costing
import net.corda.djvm.execution.ExecutionProfile
/**
* This class provides a summary of the accumulated costs for the runtime metrics that are being tracked. It also keeps
* track of applicable thresholds and will terminate sandbox execution if any of them are breached.
*
* The costs are tracked on a per-thread basis, and thus, are isolated for each sandbox. Each sandbox live on its own
* thread.
*/
class RuntimeCostSummary private constructor(
allocationCostThreshold: Long,
jumpCostThreshold: Long,
invocationCostThreshold: Long,
throwCostThreshold: Long
) {
/**
* Create a new runtime cost tracker based on an execution profile.
*/
constructor(profile: ExecutionProfile) : this(
allocationCostThreshold = profile.allocationCostThreshold,
jumpCostThreshold = profile.jumpCostThreshold,
invocationCostThreshold = profile.invocationCostThreshold,
throwCostThreshold = profile.throwCostThreshold
)
/**
* Accumulated cost of memory allocations.
*/
val allocationCost = RuntimeCost(allocationCostThreshold) {
"Sandbox [${it.name}] terminated due to over-allocation"
}
/**
* Accumulated cost of jump operations.
*/
val jumpCost = RuntimeCost(jumpCostThreshold) {
"Sandbox [${it.name}] terminated due to excessive use of looping"
}
/**
* Accumulated cost of method invocations.
*/
val invocationCost = RuntimeCost(invocationCostThreshold) {
"Sandbox [${it.name}] terminated due to excessive method calling"
}
/**
* Accumulated cost of throw operations.
*/
val throwCost = RuntimeCost(throwCostThreshold) {
"Sandbox [${it.name}] terminated due to excessive exception throwing"
}
}

View File

@ -1,73 +0,0 @@
package net.corda.djvm.costing
import net.corda.djvm.utilities.loggerFor
import sandbox.net.corda.djvm.costing.ThresholdViolationError
/**
* Cost metric to be used in a sandbox environment. The metric has a threshold and a mechanism for reporting violations.
* The implementation assumes that each metric is tracked on a per-thread basis, i.e., that each sandbox runs on its own
* thread.
*
* @param initialValue The initial value of this metric.
* @property thresholdPredicate A delegate for determining whether a threshold has been reached or not.
* @property errorMessage A delegate for generating an error message based on the thread it was reported from.
*/
open class TypedRuntimeCost<T>(
initialValue: T,
private val thresholdPredicate: (T) -> Boolean,
private val errorMessage: (Thread) -> String
) {
/**
* The thread-local container for the cost accumulator.
*/
private val costValue = object : ThreadLocal<T>() {
override fun initialValue() = initialValue
}
/**
* Property getter for accessing the current accumulated cost.
*/
val value: T
get() = costValue.get()
/**
* Helper function for doing a guarded increment of the cost value, with a mechanism for consistent error reporting
* and nuking of the current thread environment if threshold breaches are encountered.
*/
protected fun incrementAndCheck(increment: (T) -> T) {
val currentThread = getAndCheckThread() ?: return
val newValue = increment(costValue.get())
costValue.set(newValue)
if (thresholdPredicate(newValue)) {
val message = errorMessage(currentThread)
logger.error("Threshold breached; {}", message)
throw ThresholdViolationError(message)
}
}
/**
* If [filteredThreads] is specified, this method will filter out those threads whenever threshold constraints are
* being tested. This can be used to disable cost accounting on a primary thread, for instance.
*/
private fun getAndCheckThread(): Thread? {
val currentThread = Thread.currentThread()
if (filteredThreads.contains(currentThread)) {
logger.trace("Thread will not be affected by runtime costing")
return null
}
return currentThread
}
private companion object {
/**
* A set of threads to which cost accounting will be disabled.
*/
private val filteredThreads: List<Thread> = emptyList()
private val logger = loggerFor<RuntimeCost>()
}
}

View File

@ -1,37 +0,0 @@
package net.corda.djvm.execution
import net.corda.djvm.costing.RuntimeCostSummary
/**
* A read-only copy of a the costs accumulated in an [IsolatedTask].
*
* @property allocations Number of bytes allocated.
* @property invocations Number of invocations made.
* @property jumps Number of jumps made (includes conditional branches that might not have been taken).
* @property throws Number of throws made.
*/
data class CostSummary(
val allocations: Long,
val invocations: Long,
val jumps: Long,
val throws: Long
) {
/**
* Create a read-only cost summary object from an instance of [RuntimeCostSummary].
*/
constructor(costs: RuntimeCostSummary) : this(
costs.allocationCost.value,
costs.invocationCost.value,
costs.jumpCost.value,
costs.throwCost.value
)
companion object {
/**
* A blank summary of costs.
*/
val empty = CostSummary(0, 0, 0, 0)
}
}

View File

@ -1,27 +0,0 @@
package net.corda.djvm.execution
import net.corda.djvm.SandboxConfiguration
import net.corda.djvm.source.ClassSource
import java.util.function.Function
/**
* The executor is responsible for spinning up a deterministic, sandboxed environment and launching the referenced code
* block inside it. The code will run on a separate thread for complete isolation, and to enable context-based costing
* of said code. Any exceptions should be forwarded to the caller of [SandboxExecutor.run]. Similarly, the returned
* output from the referenced code block should be returned to the caller.
*
* @param configuration The configuration of the sandbox.
*/
class DeterministicSandboxExecutor<TInput, TOutput>(
configuration: SandboxConfiguration
) : SandboxExecutor<TInput, TOutput>(configuration) {
/**
* Short-hand for running a [Function] in a sandbox by its type reference.
*/
inline fun <reified TRunnable : Function<in TInput, out TOutput>> run(input: TInput):
ExecutionSummaryWithResult<TOutput> {
return run(ClassSource.fromClassName(TRunnable::class.java.name), input)
}
}

View File

@ -1,45 +0,0 @@
package net.corda.djvm.execution
/**
* The execution profile of a [java.util.function.Function] when run in a sandbox.
*
* @property allocationCostThreshold The threshold placed on allocations.
* @property invocationCostThreshold The threshold placed on invocations.
* @property jumpCostThreshold The threshold placed on jumps.
* @property throwCostThreshold The threshold placed on throw statements.
*/
enum class ExecutionProfile(
val allocationCostThreshold: Long = Long.MAX_VALUE,
val invocationCostThreshold: Long = Long.MAX_VALUE,
val jumpCostThreshold: Long = Long.MAX_VALUE,
val throwCostThreshold: Long = Long.MAX_VALUE
) {
// TODO Define sensible runtime thresholds and make further improvements to instrumentation.
/**
* Profile with a set of default thresholds.
*/
DEFAULT(
allocationCostThreshold = 1024 * 1024 * 1024,
invocationCostThreshold = 1_000_000,
jumpCostThreshold = 1_000_000,
throwCostThreshold = 1_000_000
),
/**
* Profile where no limitations have been imposed on the sandbox.
*/
UNLIMITED(),
/**
* Profile where throw statements have been disallowed.
*/
DISABLE_THROWS(throwCostThreshold = 0),
/**
* Profile where branching statements have been disallowed.
*/
DISABLE_BRANCHING(jumpCostThreshold = 0)
}

View File

@ -1,12 +0,0 @@
package net.corda.djvm.execution
/**
* The summary of the execution of a [java.util.function.Function] in a sandbox. This class has no representation of the
* outcome, and is typically used when there has been a pre-mature exit from the sandbox, for instance, if an exception
* was thrown.
*
* @property costs The costs accumulated when running the sandboxed code.
*/
open class ExecutionSummary(
val costs: CostSummary = CostSummary.empty
)

View File

@ -1,12 +0,0 @@
package net.corda.djvm.execution
/**
* The summary of the execution of a [java.util.function.Function] in a sandbox.
*
* @property result The outcome of the sandboxed operation.
* @see ExecutionSummary
*/
class ExecutionSummaryWithResult<out TResult>(
val result: TResult? = null,
costs: CostSummary = CostSummary.empty
) : ExecutionSummary(costs)

View File

@ -1,98 +0,0 @@
package net.corda.djvm.execution
import net.corda.djvm.SandboxConfiguration
import net.corda.djvm.SandboxRuntimeContext
import net.corda.djvm.messages.MessageCollection
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.djvm.rewiring.SandboxClassLoadingException
import net.corda.djvm.utilities.loggerFor
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicLong
import kotlin.concurrent.thread
/**
* Container for running a task in an isolated environment.
*/
class IsolatedTask(
private val identifier: String,
private val configuration: SandboxConfiguration
) {
/**
* Run an action in an isolated environment.
*/
fun <T> run(action: IsolatedTask.() -> T?): Result<T> {
val runnable = this
val threadName = "DJVM-$identifier-${uniqueIdentifier.getAndIncrement()}"
val completionLatch = CountDownLatch(1)
var output: T? = null
var costs = CostSummary.empty
var exception: Throwable? = null
thread(name = threadName, isDaemon = true) {
logger.trace("Entering isolated runtime environment...")
SandboxRuntimeContext(configuration).use {
output = try {
action(runnable)
} catch (ex: Throwable) {
logger.error("Exception caught in isolated runtime environment", ex)
exception = (ex as? LinkageError)?.cause ?: ex
null
}
costs = CostSummary(runtimeCosts)
}
logger.trace("Exiting isolated runtime environment...")
completionLatch.countDown()
}
completionLatch.await()
val messages = exception.let {
when (it) {
is SandboxClassLoadingException -> it.messages
is SandboxException -> {
when (it.exception) {
is SandboxClassLoadingException -> it.exception.messages
else -> null
}
}
else -> null
}
} ?: MessageCollection()
return Result(threadName, output, costs, messages, exception)
}
/**
* The result of a run of an [IsolatedTask].
*
* @property identifier The identifier of the [IsolatedTask].
* @property output The result of the run, if successful.
* @property costs Captured runtime costs as reported at the end of the run.
* @property messages The messages collated during the run.
* @property exception This holds any exceptions that might get thrown during execution.
*/
data class Result<T>(
val identifier: String,
val output: T?,
val costs: CostSummary,
val messages: MessageCollection,
val exception: Throwable?
)
/**
* The class loader to use for loading the [java.util.function.Function] and any referenced code in [SandboxExecutor.run].
*/
val classLoader: SandboxClassLoader
get() = SandboxRuntimeContext.instance.classLoader
// TODO Caching can transcend thread-local contexts by taking the sandbox configuration into account in the key derivation
private companion object {
/**
* An atomically incrementing identifier used to uniquely identify each runnable.
*/
private val uniqueIdentifier = AtomicLong(0)
private val logger = loggerFor<IsolatedTask>()
}
}

View File

@ -1,57 +0,0 @@
package net.corda.djvm.execution
import net.corda.djvm.utilities.loggerFor
import java.util.concurrent.ConcurrentLinkedQueue
/**
* Helper class for processing queued entities.
*/
@Suppress("MemberVisibilityCanBePrivate")
class QueueProcessor<T>(
private val deduplicationKeyExtractor: (T) -> String,
vararg elements: T
) {
private val queue = ConcurrentLinkedQueue<T>(elements.toMutableList())
private val seenElements = mutableSetOf<String>()
/**
* Add an element to the queue.
*/
fun enqueue(element: T) {
logger.trace("Enqueuing {}...", element)
val key = deduplicationKeyExtractor(element)
if (key !in seenElements) {
queue.add(element)
seenElements.add(key)
} else {
logger.trace("Skipped {} as it has already been processed", element)
}
}
/**
* Remove one element from the queue.
*/
fun dequeue(): T = queue.remove().apply {
logger.trace("Popping {} from the queue...", this)
}
/**
* Check if queue is empty.
*/
fun isNotEmpty() = queue.isNotEmpty()
/**
* Process the current queue with provided action per element.
*/
inline fun process(action: QueueProcessor<T>.(T) -> Unit) {
while (isNotEmpty()) {
val element = dequeue()
action(this, element)
}
}
private val logger = loggerFor<QueueProcessor<T>>()
}

View File

@ -1,34 +0,0 @@
package net.corda.djvm.execution
import net.corda.djvm.source.ClassSource
import java.io.PrintWriter
/**
* An exception raised due to a runtime error inside a sandbox.
*
* @param message The detailed message describing the problem.
* @property sandboxName The name of the sandbox in which the error occurred.
* @property sandboxClass The class used as the sandbox entry point.
* @property executionSummary A snapshot of the execution summary for the sandbox.
* @property exception The inner exception, as it was raised within the sandbox.
*/
@Suppress("MemberVisibilityCanBePrivate")
class SandboxException(
message: String,
val sandboxName: String,
val sandboxClass: ClassSource,
val executionSummary: ExecutionSummary,
val exception: Throwable
) : Exception(message, exception) {
/**
* Provide programmatic access to the stack trace information captured at the time the exception was thrown.
*/
override fun getStackTrace(): Array<out StackTraceElement>? = exception.stackTrace
/**
* Print the stack trace information of the exception using a [PrintWriter].
*/
override fun printStackTrace(writer: PrintWriter) = exception.printStackTrace(writer)
}

Some files were not shown because too many files have changed in this diff Show More