Refactoring: extract method
This commit is contained in:
@@ -87,28 +87,55 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
) : Evaluator {
|
) : Evaluator {
|
||||||
override fun evaluate(context: EvaluationContextImpl): Any? {
|
override fun evaluate(context: EvaluationContextImpl): Any? {
|
||||||
try {
|
try {
|
||||||
|
val compiledData = extractAndCompile(codeFragment, sourcePosition)
|
||||||
|
return runEval4j(context, compiledData)
|
||||||
|
}
|
||||||
|
catch(e: EvaluateException) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
logger.error("Couldn't evaluate expression:\nfileText = ${sourcePosition.getFile().getText()}\nline = ${sourcePosition.getLine()}\ncodeFragment = ${codeFragment.getText()}", e)
|
||||||
|
val cause = if (e.getMessage() != null) ": ${e.getMessage()}" else ""
|
||||||
|
exception("An exception occurs during Evaluate Expression Action $cause")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getModifier(): Modifier? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
class object {
|
||||||
|
private fun extractAndCompile(codeFragment: JetCodeFragment, sourcePosition: SourcePosition): CompiledDataDescriptor {
|
||||||
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
|
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
|
||||||
if (extractedFunction == null) {
|
if (extractedFunction == null) {
|
||||||
throw IllegalStateException("Code fragment cannot be extracted to function: ${sourcePosition.getFile().getText()}:${sourcePosition.getLine()},\ncodeFragment = ${codeFragment.getText()}")
|
throw IllegalStateException("Code fragment cannot be extracted to function: ${sourcePosition.getFile().getText()}:${sourcePosition.getLine()},\ncodeFragment = ${codeFragment.getText()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val classFileFactory = createClassFileFactory(extractedFunction)
|
val classFileFactory = createClassFileFactory(codeFragment, extractedFunction)
|
||||||
|
|
||||||
// KT-4509
|
// KT-4509
|
||||||
val outputFiles = (classFileFactory : OutputFileCollection).asList().filter { it.relativePath != "$packageInternalName.class" }
|
val outputFiles = (classFileFactory : OutputFileCollection).asList().filter { it.relativePath != "$packageInternalName.class" }
|
||||||
if (outputFiles.size() != 1) exception("Expression compiles to more than one class file. Note that lambdas, classes and objects are unsupported yet. List of files: ${outputFiles.makeString(",")}")
|
if (outputFiles.size() != 1) exception("Expression compiles to more than one class file. Note that lambdas, classes and objects are unsupported yet. List of files: ${outputFiles.makeString(",")}")
|
||||||
|
|
||||||
|
val funName = extractedFunction.getName()
|
||||||
|
if (funName == null) {
|
||||||
|
throw IllegalStateException("Extracted function should have a name: ${extractedFunction.getText()}")
|
||||||
|
}
|
||||||
|
return CompiledDataDescriptor(outputFiles.first().asByteArray(), funName, extractedFunction.getParameterNamesForDebugger())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runEval4j(context: EvaluationContextImpl, compiledData: CompiledDataDescriptor): Any? {
|
||||||
val virtualMachine = context.getDebugProcess().getVirtualMachineProxy().getVirtualMachine()
|
val virtualMachine = context.getDebugProcess().getVirtualMachineProxy().getVirtualMachine()
|
||||||
|
|
||||||
var resultValue: Value? = null
|
var resultValue: Value? = null
|
||||||
ClassReader(outputFiles.first().asByteArray()).accept(object : ClassVisitor(ASM5) {
|
ClassReader(compiledData.bytecodes).accept(object : ClassVisitor(ASM5) {
|
||||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||||
if (name == extractedFunction.getName()) {
|
if (name == compiledData.funName) {
|
||||||
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
||||||
override fun visitEnd() {
|
override fun visitEnd() {
|
||||||
val value = interpreterLoop(
|
val value = interpreterLoop(
|
||||||
this,
|
this,
|
||||||
makeInitialFrame(this, context.getArgumentsByNames(extractedFunction.getParameterNamesForDebugger())),
|
makeInitialFrame(this, context.getArgumentsByNames(compiledData.parameterNames)),
|
||||||
JDIEval(virtualMachine,
|
JDIEval(virtualMachine,
|
||||||
context.getClassLoader()!!,
|
context.getClassLoader()!!,
|
||||||
context.getSuspendContext().getThread()?.getThreadReference()!!, context.getSuspendContext().getInvokePolicy())
|
context.getSuspendContext().getThread()?.getThreadReference()!!, context.getSuspendContext().getInvokePolicy())
|
||||||
@@ -128,104 +155,92 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
if (resultValue == null) {
|
if (resultValue == null) {
|
||||||
throw IllegalStateException("resultValue is null: cannot find method ${extractedFunction.getName()} in ${outputFiles.first().relativePath}")
|
throw IllegalStateException("resultValue is null: cannot find method ${compiledData.funName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultValue!!.asJdiValue(virtualMachine, resultValue!!.asmType)
|
return resultValue!!.asJdiValue(virtualMachine, resultValue!!.asmType)
|
||||||
}
|
}
|
||||||
catch(e: EvaluateException) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
catch (e: Exception) {
|
|
||||||
logger.error("Couldn't evaluate expression:\nfileText = ${sourcePosition.getFile().getText()}\nline = ${sourcePosition.getLine()}\ncodeFragment = ${codeFragment.getText()}", e)
|
|
||||||
val cause = if (e.getMessage() != null) ": ${e.getMessage()}" else ""
|
|
||||||
exception("An exception occurs during Evaluate Expression Action $cause")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getModifier(): Modifier? {
|
private fun SuspendContext.getInvokePolicy(): Int {
|
||||||
return null
|
return if (getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD) ObjectReference.INVOKE_SINGLE_THREADED else 0
|
||||||
}
|
|
||||||
|
|
||||||
private fun SuspendContext.getInvokePolicy(): Int {
|
|
||||||
return if (getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD) ObjectReference.INVOKE_SINGLE_THREADED else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun JetNamedFunction.getParameterNamesForDebugger(): List<String> {
|
|
||||||
val result = arrayListOf<String>()
|
|
||||||
if (getReceiverTypeRef() != null) {
|
|
||||||
result.add("this")
|
|
||||||
}
|
}
|
||||||
for (param in getValueParameters()) {
|
|
||||||
result.add(param.getName()!!)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun EvaluationContextImpl.getArgumentsByNames(parameterNames: List<String>): List<Value> {
|
private fun JetNamedFunction.getParameterNamesForDebugger(): List<String> {
|
||||||
val frames = getFrameProxy()?.getStackFrame()
|
val result = arrayListOf<String>()
|
||||||
if (frames != null) {
|
if (getReceiverTypeRef() != null) {
|
||||||
fun getValue(name: String): Value {
|
result.add("this")
|
||||||
return try {
|
|
||||||
when (name) {
|
|
||||||
"this" -> frames.thisObject().asValue()
|
|
||||||
else -> frames.getValue(frames.visibleVariableByName(name)).asValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e: Exception) {
|
|
||||||
exception("Cannot get parameter value from local variables table: parameterName = ${name}. Note that captured parameters are unsupported yet.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
for (param in getValueParameters()) {
|
||||||
return parameterNames.map { getValue(it) }
|
result.add(param.getName()!!)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return Collections.emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createClassFileFactory(extractedFunction: JetNamedFunction): ClassFileFactory {
|
private fun EvaluationContextImpl.getArgumentsByNames(parameterNames: List<String>): List<Value> {
|
||||||
return ApplicationManager.getApplication()?.runReadAction(object: Computable<ClassFileFactory> {
|
val frames = getFrameProxy()?.getStackFrame()
|
||||||
override fun compute(): ClassFileFactory? {
|
if (frames != null) {
|
||||||
val file = createFileForDebugger(codeFragment, extractedFunction)
|
fun getValue(name: String): Value {
|
||||||
|
return try {
|
||||||
checkForSyntacticErrors(file)
|
when (name) {
|
||||||
|
"this" -> frames.thisObject().asValue()
|
||||||
val analyzeExhaust = file.getAnalysisResults()
|
else -> frames.getValue(frames.visibleVariableByName(name)).asValue()
|
||||||
if (analyzeExhaust.isError()) {
|
}
|
||||||
exception(analyzeExhaust.getError())
|
}
|
||||||
}
|
catch(e: Exception) {
|
||||||
|
exception("Cannot get parameter value from local variables table: parameterName = ${name}. Note that captured parameters are unsupported yet.")
|
||||||
val bindingContext = analyzeExhaust.getBindingContext()
|
|
||||||
bindingContext.getDiagnostics().forEach {
|
|
||||||
diagnostic ->
|
|
||||||
if (diagnostic.getSeverity() == Severity.ERROR) {
|
|
||||||
exception(DefaultErrorMessages.RENDERER.render(diagnostic))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val state = GenerationState(
|
return parameterNames.map { getValue(it) }
|
||||||
file.getProject(),
|
|
||||||
ClassBuilderFactories.BINARIES,
|
|
||||||
analyzeExhaust.getModuleDescriptor(),
|
|
||||||
bindingContext,
|
|
||||||
listOf(file)
|
|
||||||
)
|
|
||||||
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
|
||||||
|
|
||||||
return state.getFactory()
|
|
||||||
}
|
}
|
||||||
})!!
|
return Collections.emptyList()
|
||||||
|
}
|
||||||
}
|
|
||||||
|
private fun createClassFileFactory(codeFragment: JetCodeFragment, extractedFunction: JetNamedFunction): ClassFileFactory {
|
||||||
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
return ApplicationManager.getApplication()?.runReadAction(object : Computable<ClassFileFactory> {
|
||||||
|
override fun compute(): ClassFileFactory? {
|
||||||
private fun exception(e: Throwable) {
|
val file = createFileForDebugger(codeFragment, extractedFunction)
|
||||||
val message = e.getMessage()
|
|
||||||
if (message != null) {
|
checkForSyntacticErrors(file)
|
||||||
exception(message)
|
|
||||||
|
val analyzeExhaust = file.getAnalysisResults()
|
||||||
|
if (analyzeExhaust.isError()) {
|
||||||
|
exception(analyzeExhaust.getError())
|
||||||
|
}
|
||||||
|
|
||||||
|
val bindingContext = analyzeExhaust.getBindingContext()
|
||||||
|
bindingContext.getDiagnostics().forEach {
|
||||||
|
diagnostic ->
|
||||||
|
if (diagnostic.getSeverity() == Severity.ERROR) {
|
||||||
|
exception(DefaultErrorMessages.RENDERER.render(diagnostic))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = GenerationState(
|
||||||
|
file.getProject(),
|
||||||
|
ClassBuilderFactories.BINARIES,
|
||||||
|
analyzeExhaust.getModuleDescriptor(),
|
||||||
|
bindingContext,
|
||||||
|
listOf(file)
|
||||||
|
)
|
||||||
|
|
||||||
|
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
|
||||||
|
return state.getFactory()
|
||||||
|
}
|
||||||
|
})!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
||||||
|
|
||||||
|
private fun exception(e: Throwable) {
|
||||||
|
val message = e.getMessage()
|
||||||
|
if (message != null) {
|
||||||
|
exception(message)
|
||||||
|
}
|
||||||
|
throw EvaluateExceptionUtil.createEvaluateException(e)
|
||||||
}
|
}
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,3 +283,6 @@ fun checkForSyntacticErrors(file: JetFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class CompiledDataDescriptor(val bytecodes: ByteArray, val funName: String, val parameterNames: List<String>)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user