Skip to content

Commit

Permalink
- new feature
Browse files Browse the repository at this point in the history
- choose dynamically compiler based on judge request
  • Loading branch information
spolnik committed Feb 3, 2017
1 parent 398841c commit 3102394
Show file tree
Hide file tree
Showing 16 changed files with 63 additions and 105 deletions.
2 changes: 1 addition & 1 deletion gradle/versioning.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//noinspection GroovyAssignabilityCheck
version = new ProjectVersion(
'1', '0', '20', System.env.TRAVIS_BUILD_NUMBER
'1', '0', '21', System.env.TRAVIS_BUILD_NUMBER
)

println "Version number: " + version
Expand Down
9 changes: 9 additions & 0 deletions src/main/kotlin/com/jalgoarena/ApplicationConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.jalgoarena.codegeneration.JavaCodeGenerator
import com.jalgoarena.codegeneration.JvmCodeGenerator
import com.jalgoarena.codegeneration.KotlinCodeGenerator
import com.jalgoarena.compile.JvmCompiler
import com.jalgoarena.compile.KotlinCompiler
import com.jalgoarena.compile.InMemoryJavaCompiler
import com.jalgoarena.type.ListNode
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
Expand Down Expand Up @@ -34,4 +37,10 @@ open class ApplicationConfiguration {
JavaCodeGenerator(),
KotlinCodeGenerator()
)

@Bean
open fun codeCompilers(): List<JvmCompiler> = listOf(
InMemoryJavaCompiler(),
KotlinCompiler()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import java.io.IOException
import java.nio.CharBuffer
import javax.tools.*

class MemoryJavaCompiler : JvmCompiler {
class InMemoryJavaCompiler : JvmCompiler {

private val LOG = LoggerFactory.getLogger(this.javaClass)

private fun standardFileManager(): StandardJavaFileManager {
return javaCompiler.getStandardFileManager(null, null, null)
}
override fun programmingLanguage() = "java"

override fun run(className: String, source: String): MutableMap<String, ByteArray?> {

Expand Down Expand Up @@ -41,6 +39,10 @@ class MemoryJavaCompiler : JvmCompiler {
return classBytes
}

private fun standardFileManager(): StandardJavaFileManager {
return javaCompiler.getStandardFileManager(null, null, null)
}

private fun compilationTask(
fileName: String,
source: String,
Expand Down
13 changes: 0 additions & 13 deletions src/main/kotlin/com/jalgoarena/compile/IsKotlinSourceCode.kt

This file was deleted.

2 changes: 2 additions & 0 deletions src/main/kotlin/com/jalgoarena/compile/JvmCompiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ interface JvmCompiler {
throw NoSuchMethodError(methodName)
}

fun programmingLanguage(): String

fun run(className: String, source: String): MutableMap<String, ByteArray?>

private class MemoryClassLoader(val classNameToBytecode: MutableMap<String, ByteArray?>)
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/jalgoarena/compile/KotlinCompiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class KotlinCompiler : JvmCompiler {

private val compiler = K2JVMCompiler()

override fun programmingLanguage() = "kotlin"

override fun run(className: String, source: String): MutableMap<String, ByteArray?> {

val tmpDir = createTmpDir()
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/com/jalgoarena/judge/JudgeEngine.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.jalgoarena.judge

import com.jalgoarena.domain.JudgeRequest
import com.jalgoarena.domain.JudgeResult
import com.jalgoarena.domain.Problem

interface JudgeEngine {
fun judge(problem: Problem, userCode: String): JudgeResult
fun judge(problem: Problem, judgeRequest: JudgeRequest): JudgeResult
}
26 changes: 14 additions & 12 deletions src/main/kotlin/com/jalgoarena/judge/JvmJudgeEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.jalgoarena.judge
import com.fasterxml.jackson.databind.ObjectMapper
import com.jalgoarena.compile.*
import com.jalgoarena.domain.Function
import com.jalgoarena.domain.JudgeRequest
import com.jalgoarena.domain.JudgeResult
import com.jalgoarena.domain.JudgeResult.*
import com.jalgoarena.domain.Problem
Expand All @@ -13,22 +14,26 @@ import java.util.concurrent.*
import javax.inject.Inject

@Component
open class JvmJudgeEngine(@Inject private val objectMapper: ObjectMapper) : JudgeEngine {
open class JvmJudgeEngine(
@Inject private val objectMapper: ObjectMapper,
@Inject private val compilers: List<JvmCompiler>
) : JudgeEngine {

private val NUMBER_OF_ITERATIONS = 5

override fun judge(problem: Problem, userCode: String): JudgeResult {
override fun judge(problem: Problem, judgeRequest: JudgeRequest): JudgeResult {

val isKotlin = IsKotlinSourceCode().findIn(userCode, problem.function!!)
val className = findClassName(isKotlin, userCode)
val (sourceCode, userId, language) = judgeRequest

val className = findClassName(language, sourceCode)

if (!className.isPresent) {
return CompileError("ClassNotFoundException: No public class found")
}

val compiler = if (isKotlin) KotlinCompiler() else MemoryJavaCompiler()
val compiler = compilers.first { it.programmingLanguage() == language }

return compileAndJudge(className, compiler, problem.function, problem, userCode)
return compileAndJudge(className, compiler, problem.function!!, problem, judgeRequest.sourceCode)
}

private fun judge(clazz: Any, method: Method, problem: Problem): JudgeResult {
Expand Down Expand Up @@ -106,12 +111,9 @@ open class JvmJudgeEngine(@Inject private val objectMapper: ObjectMapper) : Judg
}
}

private fun findClassName(
isKotlin: Boolean,
userCode: String
): Optional<String> = when (isKotlin) {
true -> userCode.findKotlinClassName()
false -> userCode.findJavaClassName()
private fun findClassName(language: String, sourceCode: String) = when (language) {
"kotlin" -> sourceCode.findKotlinClassName()
else -> sourceCode.findJavaClassName()
}

private fun readInternalTestCases(problem: Problem): Array<InternalTestCase> {
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/jalgoarena/web/JudgeController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class JudgeController(
@RequestBody judgeRequest: JudgeRequest,
@RequestHeader("X-Authorization", required = false) token: String? = null
): JudgeResult {
val judgeResult = judgeEngine.judge(problemsRepository.find(id), judgeRequest.sourceCode)
val judgeResult = judgeEngine.judge(problemsRepository.find(id), judgeRequest)

return when {
judgeResult.statusCode == StatusCode.ACCEPTED.toString() ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class JudgeControllerIntegrationTest {
@Parameters("2-sum, TwoSum", "fib, FibFast", "stoi, MyStoi", "word-ladder, WordLadder", "is-string-unique, IsStringUnique2", "check-perm, CheckPerm", "palindrome-perm, PalindromePerm", "one-away, OneAway", "string-compress, StringCompress", "rotate-matrix, RotateMatrix", "zero-matrix, ZeroMatrix", "remove-dups, RemoveDups", "kth-to-last, KThToLast", "string-rotation, StringRotation", "sum-lists, SumLists", "sum-lists-2, SumLists2", "palindrome-list, PalindromeList", "binary-search, BinarySearch", "delete-tail-node, DeleteTailNode", "repeated-elements, RepeatedElements", "first-non-repeated-char, FirstNonRepeatedChar", "find-middle-node, FindMiddleNode", "horizontal-flip, HorizontalFlip", "vertical-flip, VerticalFlip", "single-number, SingleNumber", "preorder-traversal, PreorderTraversal", "inorder-traversal, InorderTraversal", "postorder-traversal, PostorderTraversal", "height-binary-tree, HeightOfBinaryTree", "sum-binary-tree, SumBinaryTree", "insert-stars, InsertStars", "transpose-matrix, TransposeMatrix", "merge-k-sorted-linked-lists, MergeKSortedLinkedLists")
fun judgesKotlinCorrectSolution(problemId: String, solutionId: String) {
val sourceCode = Resources.toString(Resources.getResource(solutionId + ".kt"), Charsets.UTF_8)
val result = judgeController.judge(problemId, JudgeRequest(sourceCode, "0-0", "java"))
val result = judgeController.judge(problemId, JudgeRequest(sourceCode, "0-0", "kotlin"))

assertThat(result.statusCode).isEqualTo(StatusCode.ACCEPTED.toString())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.jalgoarena.config
import com.fasterxml.jackson.databind.ObjectMapper
import com.jalgoarena.ApplicationConfiguration
import com.jalgoarena.codegeneration.JvmCodeGenerator
import com.jalgoarena.compile.InMemoryJavaCompiler
import com.jalgoarena.compile.KotlinCompiler
import com.jalgoarena.data.ProblemsRepository
import com.jalgoarena.data.SubmissionsRepository
import com.jalgoarena.domain.Submission
Expand All @@ -20,7 +22,9 @@ open class TestApplicationConfiguration : ApplicationConfiguration() {
ProblemsClientForTests()

@Bean
open fun judgeEngine(objectMapper: ObjectMapper) = JvmJudgeEngine(objectMapper)
open fun judgeEngine(objectMapper: ObjectMapper) = JvmJudgeEngine(objectMapper, listOf(
InMemoryJavaCompiler(), KotlinCompiler()
))

@Bean
open fun submissionsRepository() = FakeSubmissionRepository()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.jalgoarena.judge

import com.jalgoarena.compile.CompileErrorException
import com.jalgoarena.compile.MemoryJavaCompiler
import com.jalgoarena.compile.InMemoryJavaCompiler
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class MemoryJavaCompilerTest {
class InMemoryJavaCompilerTest {

private val SOURCE_CODE =
"public final class Solution {\npublic static String greeting(String name) {\n\treturn \"Hello \" + name;\n}\n}\n"

@Test
fun compileAndRunStaticMethod() {

val (instance, method) = MemoryJavaCompiler().compileMethod(
val (instance, method) = InMemoryJavaCompiler().compileMethod(
"Solution", "greeting", 1, SOURCE_CODE
)

Expand All @@ -23,7 +23,7 @@ class MemoryJavaCompilerTest {

@Test(expected = NoSuchMethodError::class)
fun throwsNoSuchMethodExceptionWhenInvokingNonExistingMethod() {
MemoryJavaCompiler().compileMethod(
InMemoryJavaCompiler().compileMethod(
"Solution", "dummy", 1, SOURCE_CODE
)
}
Expand All @@ -32,7 +32,7 @@ class MemoryJavaCompilerTest {
fun throwsIllegalStateExceptionWhenTryingToInvokeClassWithPrivateConstructor() {
val sourceCode = "public final class Solution { private Solution() { }; public void dummy() {} }"

MemoryJavaCompiler().compileMethod(
InMemoryJavaCompiler().compileMethod(
"Solution", "dummy", 0, sourceCode
)
}
Expand All @@ -41,7 +41,7 @@ class MemoryJavaCompilerTest {
fun throwsCompileErrorExceptionWhenSourceCodeDoesNotCompile() {
val sourceCodeMissingReturnString = "public final class Solution { private Solution() { }; public String dummy() {} }"

MemoryJavaCompiler().compileMethod(
InMemoryJavaCompiler().compileMethod(
"Solution", "dummy", 0, sourceCodeMissingReturnString
)
}
Expand Down
61 changes: 0 additions & 61 deletions src/test/kotlin/com/jalgoarena/judge/IsKotlinSourceCodeTest.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.jalgoarena.judge
import com.google.common.io.Resources
import com.jalgoarena.config.TestApplicationConfiguration
import com.jalgoarena.data.ProblemsRepository
import com.jalgoarena.domain.JudgeRequest
import com.jalgoarena.domain.StatusCode
import junitparams.JUnitParamsRunner
import junitparams.Parameters
Expand Down Expand Up @@ -67,7 +68,7 @@ open class JavaEngineIntegrationTest {
val problem = repository.find(problemId)
val sourceCode = Resources.toString(Resources.getResource("$solutionId.java"), Charsets.UTF_8)

val result = judgeEngine.judge(problem, sourceCode)
val result = judgeEngine.judge(problem, JudgeRequest(sourceCode, "0-0", "java"))

assertThat(result.statusCode).isEqualTo(statusCode.toString())
} catch (e: Exception) {
Expand Down
13 changes: 11 additions & 2 deletions src/test/kotlin/com/jalgoarena/web/JudgeControllerSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.jalgoarena.compile.InMemoryJavaCompiler
import com.jalgoarena.compile.JvmCompiler
import com.jalgoarena.compile.KotlinCompiler
import com.jalgoarena.config.TestApplicationConfiguration
import com.jalgoarena.data.ProblemsRepository
import com.jalgoarena.domain.Problem
Expand Down Expand Up @@ -135,10 +138,16 @@ class JudgeControllerSpec {
}

@Bean
open fun judgeEngine(objectMapper: ObjectMapper) = JvmJudgeEngine(objectMapper)
open fun judgeEngine(objectMapper: ObjectMapper, codeCompilers: List<JvmCompiler>) =
JvmJudgeEngine(objectMapper, codeCompilers)

@Bean
open fun submissionsRepository() = TestApplicationConfiguration.FakeSubmissionRepository()

@Bean
open fun codeCompilers() = listOf(
InMemoryJavaCompiler(), KotlinCompiler()
)
}

//language=JSON
Expand Down Expand Up @@ -219,7 +228,7 @@ class JudgeControllerSpec {
private fun judgeRequest(sourceCode: String) = """{
"sourceCode": "${StringEscapeUtils.escapeJava(sourceCode)}",
"userId": "0-0",
"language": "java"
"language": "kotlin"
}
"""

Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/com/jalgoarena/web/JudgeControllerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class JudgeControllerTest {
FIB_PROBLEM
)

given(judgeEngine.judge(FIB_PROBLEM, DUMMY_SOURCE_CODE)).willReturn(
given(judgeEngine.judge(FIB_PROBLEM, JudgeRequest(DUMMY_SOURCE_CODE, "0-0", "java"))).willReturn(
JudgeResult.Accepted(NUMBER_OF_TEST_CASES, ELAPSED_TIME, USED_MEMORY)
)

Expand Down

0 comments on commit 3102394

Please sign in to comment.