mirror of
https://github.com/corda/corda.git
synced 2025-04-07 11:27:01 +00:00
CORDA-2775: Simplify SourceClassLoader logic for DJVM. (#4913)
This commit is contained in:
parent
2777e32a1c
commit
9ebe464f63
@ -44,7 +44,7 @@ dependencies {
|
||||
jar.enabled = false
|
||||
|
||||
shadowJar {
|
||||
baseName'corda-djvm'
|
||||
baseName 'corda-djvm'
|
||||
classifier ''
|
||||
relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm'
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()) {
|
||||
|
||||
/**
|
||||
|
@ -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].
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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) }
|
||||
|
Loading…
x
Reference in New Issue
Block a user