CORDA-2775: Simplify SourceClassLoader logic for DJVM. (#4913)

This commit is contained in:
Chris Rankin 2019-03-22 09:27:32 +00:00 committed by Tommy Lillehagen
parent 2777e32a1c
commit 9ebe464f63
10 changed files with 58 additions and 87 deletions

View File

@ -44,7 +44,7 @@ dependencies {
jar.enabled = false
shadowJar {
baseName'corda-djvm'
baseName 'corda-djvm'
classifier ''
relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm'

View File

@ -8,7 +8,6 @@ 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.AbstractSourceClassLoader
import net.corda.djvm.source.BootstrapClassLoader
import net.corda.djvm.source.SourceClassLoader
import org.objectweb.asm.Opcodes.*
@ -48,7 +47,7 @@ class AnalysisConfiguration private constructor(
val classModule: ClassModule,
val memberModule: MemberModule,
private val bootstrapClassLoader: BootstrapClassLoader?,
val supportingClassLoader: AbstractSourceClassLoader,
val supportingClassLoader: SourceClassLoader,
private val isRootConfiguration: Boolean
) : Closeable {
@ -299,7 +298,7 @@ class AnalysisConfiguration private constructor(
classModule: ClassModule = ClassModule(),
memberModule: MemberModule = MemberModule(),
bootstrapClassLoader: BootstrapClassLoader? = null,
sourceClassLoaderFactory: (ClassResolver, BootstrapClassLoader?) -> AbstractSourceClassLoader = { classResolver, bootstrapCL ->
sourceClassLoaderFactory: (ClassResolver, BootstrapClassLoader?) -> SourceClassLoader = { classResolver, bootstrapCL ->
SourceClassLoader(emptyList(), classResolver, bootstrapCL)
}
): AnalysisConfiguration {

View File

@ -118,7 +118,7 @@ class ClassResolver(
/**
* Maps a class name to its equivalent class outside the sandbox.
* Needed by [net.corda.djvm.source.AbstractSourceClassLoader].
* Needed by [net.corda.djvm.source.SourceClassLoader].
*/
private fun toSource(className: String): String {
return if (className in pinnedClasses) {

View File

@ -7,7 +7,7 @@ import net.corda.djvm.code.ClassMutator
import net.corda.djvm.code.EmitterModule
import net.corda.djvm.code.emptyAsNull
import net.corda.djvm.references.Member
import net.corda.djvm.source.AbstractSourceClassLoader
import net.corda.djvm.source.SourceClassLoader
import net.corda.djvm.utilities.loggerFor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
@ -22,7 +22,7 @@ import org.objectweb.asm.MethodVisitor
*/
open class ClassRewriter(
private val configuration: SandboxConfiguration,
private val classLoader: AbstractSourceClassLoader
private val classLoader: SourceClassLoader
) {
private val analysisConfig = configuration.analysisConfiguration

View File

@ -9,8 +9,8 @@ import net.corda.djvm.analysis.ExceptionResolver.Companion.isDJVMException
import net.corda.djvm.code.asPackagePath
import net.corda.djvm.code.asResourcePath
import net.corda.djvm.references.ClassReference
import net.corda.djvm.source.AbstractSourceClassLoader
import net.corda.djvm.source.ClassSource
import net.corda.djvm.source.SourceClassLoader
import net.corda.djvm.utilities.loggerFor
import net.corda.djvm.validation.RuleValidator
import org.objectweb.asm.Type
@ -27,13 +27,13 @@ import org.objectweb.asm.Type
* @param parent This classloader's parent classloader.
*/
class SandboxClassLoader private constructor(
private val analysisConfiguration: AnalysisConfiguration,
private val ruleValidator: RuleValidator,
private val supportingClassLoader: AbstractSourceClassLoader,
private val rewriter: ClassRewriter,
private val context: AnalysisContext,
throwableClass: Class<*>?,
parent: ClassLoader?
private val analysisConfiguration: AnalysisConfiguration,
private val ruleValidator: RuleValidator,
private val supportingClassLoader: SourceClassLoader,
private val rewriter: ClassRewriter,
private val context: AnalysisContext,
throwableClass: Class<*>?,
parent: ClassLoader?
) : ClassLoader(parent ?: getSystemClassLoader()) {
/**

View File

@ -1,7 +1,7 @@
package net.corda.djvm.rewiring
import net.corda.djvm.code.asPackagePath
import net.corda.djvm.source.AbstractSourceClassLoader
import net.corda.djvm.source.SourceClassLoader
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES
@ -23,11 +23,11 @@ import org.objectweb.asm.Type
*/
open class SandboxClassWriter(
classReader: ClassReader,
private val cloader: AbstractSourceClassLoader,
private val cloader: SourceClassLoader,
flags: Int = COMPUTE_FRAMES or COMPUTE_MAXS
) : ClassWriter(classReader, flags) {
override fun getClassLoader(): AbstractSourceClassLoader = cloader
override fun getClassLoader(): SourceClassLoader = cloader
/**
* Get the common super type of [type1] and [type2].

View File

@ -1,7 +1,6 @@
@file:JvmName("SourceClassLoaderTools")
package net.corda.djvm.source
import net.corda.djvm.analysis.AnalysisConfiguration.Companion.SANDBOX_PREFIX
import net.corda.djvm.analysis.AnalysisContext
import net.corda.djvm.analysis.ClassResolver
import net.corda.djvm.analysis.ExceptionResolver.Companion.getDJVMExceptionOwner
@ -22,17 +21,51 @@ import java.nio.file.Path
import java.nio.file.Paths
import kotlin.streams.toList
abstract class AbstractSourceClassLoader(
/**
* Class loader to manage an optional JAR of replacement Java APIs.
* @param bootstrapJar The location of the JAR containing the Java APIs.
*/
class BootstrapClassLoader(
bootstrapJar: Path
) : URLClassLoader(resolvePaths(listOf(bootstrapJar)), null) {
/**
* Only search our own jars for the given resource.
*/
override fun getResource(name: String): URL? = findResource(name)
}
/**
* Customizable class loader that allows the user to specify explicitly additional JARs and directories to scan.
*
* @param paths The directories and explicit JAR files to scan.
* @property classResolver The resolver to use to derive the original name of a requested class.
* @property bootstrap The [BootstrapClassLoader] containing the Java APIs for the sandbox.
*/
class SourceClassLoader private constructor(
paths: List<Path>,
private val classResolver: ClassResolver,
private val bootstrap: BootstrapClassLoader?,
parent: ClassLoader?
) : URLClassLoader(resolvePaths(paths), parent) {
private companion object {
private val logger = loggerFor<SourceClassLoader>()
}
constructor(paths: List<Path>, classResolver: ClassResolver, bootstrap: BootstrapClassLoader? = null)
:this(paths, classResolver, bootstrap, SourceClassLoader::class.java.classLoader)
/**
* An empty [SourceClassLoader] that can only delegate to its [BootstrapClassLoader].
*/
constructor(classResolver: ClassResolver, bootstrap: BootstrapClassLoader)
: this(emptyList(), classResolver, bootstrap, null)
/**
* Open a [ClassReader] for the provided class name.
*/
fun classReader(
className: String, context: AnalysisContext, origin: String? = null
className: String, context: AnalysisContext, origin: String? = null
): ClassReader {
val originalName = classResolver.reverse(className.asResourcePath)
@ -73,70 +106,6 @@ abstract class AbstractSourceClassLoader(
return loadClass(originalName)
}
protected companion object {
@JvmStatic
protected val logger = loggerFor<SourceClassLoader>()
}
}
/**
* Class loader to manage an optional JAR of replacement Java APIs.
* @param bootstrapJar The location of the JAR containing the Java APIs.
*/
class BootstrapClassLoader(
bootstrapJar: Path
) : URLClassLoader(resolvePaths(listOf(bootstrapJar)), null) {
/**
* Only search our own jars for the given resource.
*/
override fun getResource(name: String): URL? = findResource(name)
}
/**
* Class loader that only provides our built-in sandbox classes.
* @param classResolver The resolver to use to derive the original name of a requested class.
*/
class SandboxSourceClassLoader(
classResolver: ClassResolver,
private val bootstrap: BootstrapClassLoader
) : AbstractSourceClassLoader(emptyList(), classResolver, SandboxSourceClassLoader::class.java.classLoader) {
/**
* Always check the bootstrap classloader first. If we're requesting
* built-in sandbox classes then delegate to our parent classloader,
* otherwise deny the request.
*/
override fun getResource(name: String): URL? {
val resource = bootstrap.findResource(name)
if (resource != null) {
return resource
} else if (isJvmInternal(name)) {
logger.error("Denying request for actual {}", name)
return null
}
return if (name.startsWith(SANDBOX_PREFIX)) {
parent.getResource(name)
} else {
null
}
}
}
/**
* Customizable class loader that allows the user to explicitly specify additional JARs and directories to scan.
*
* @param paths The directories and explicit JAR files to scan.
* @property classResolver The resolver to use to derive the original name of a requested class.
* @property bootstrap The [BootstrapClassLoader] containing the Java APIs for the sandbox.
*/
class SourceClassLoader(
paths: List<Path>,
classResolver: ClassResolver,
private val bootstrap: BootstrapClassLoader? = null
) : AbstractSourceClassLoader(paths, classResolver, SourceClassLoader::class.java.classLoader) {
/**
* First check the bootstrap classloader, if we have one.
* Otherwise check our parent classloader, followed by

View File

@ -16,6 +16,8 @@ public class SandboxExecutorJavaTest extends TestBase {
@Test
public void testTransaction() {
//TODO: Transaction should not be a pinned class! It needs
// to be marshalled into and out of the sandbox.
Set<Class<?>> pinnedClasses = singleton(Transaction.class);
sandbox(new Object[0], pinnedClasses, WARNING, true, ctx -> {
SandboxExecutor<Transaction, Void> contractExecutor = new DeterministicSandboxExecutor<>(ctx.getConfiguration());

View File

@ -17,7 +17,7 @@ import net.corda.djvm.rules.Rule
import net.corda.djvm.rules.implementation.*
import net.corda.djvm.source.BootstrapClassLoader
import net.corda.djvm.source.ClassSource
import net.corda.djvm.source.SandboxSourceClassLoader
import net.corda.djvm.source.SourceClassLoader
import net.corda.djvm.utilities.Discovery
import net.corda.djvm.validation.RuleValidator
import org.junit.After
@ -88,7 +88,7 @@ abstract class TestBase {
Whitelist.MINIMAL,
bootstrapClassLoader = BootstrapClassLoader(DETERMINISTIC_RT),
sourceClassLoaderFactory = { classResolver, bootstrapClassLoader ->
SandboxSourceClassLoader(classResolver, bootstrapClassLoader!!)
SourceClassLoader(classResolver, bootstrapClassLoader!!)
},
additionalPinnedClasses = setOf(
Utilities::class.java

View File

@ -37,6 +37,7 @@ class SandboxExecutorTest : TestBase() {
@Test
fun `can load and execute contract`() = sandbox(DEFAULT, pinnedClasses = setOf(Transaction::class.java)) {
val contractExecutor = DeterministicSandboxExecutor<Transaction, Unit>(configuration)
//TODO: Transaction should not be a pinned class! It needs to be marshalled into and out of the sandbox.
val tx = Transaction(1)
assertThatExceptionOfType(SandboxException::class.java)
.isThrownBy { contractExecutor.run<Contract>(tx) }