Compare commits

...

19 Commits

Author SHA1 Message Date
Yan Zhulanow
3e4c541f25 REPL, Minor: Rename ':dump bytecode' to ':classes' 2018-02-07 18:45:42 +03:00
Yan Zhulanow
fe3ed7c97a REPL, Minor: Refactor help message 2018-02-07 18:45:41 +03:00
Yan Zhulanow
a75f560134 REPL: Use single classloader in all REPL places 2018-02-07 18:45:40 +03:00
Yan Zhulanow
dd1cb28d3e REPL: Reuse classloader between snippets 2018-02-07 18:45:39 +03:00
Yan Zhulanow
526cdb9257 REPL: Ignore empty lines (KT-21611) 2018-02-07 18:45:38 +03:00
Yan Zhulanow
aac1e2a24a REPL: Forbid 'this' in REPL lines (KT-14851, KT-12569) 2018-02-07 18:45:37 +03:00
Yan Zhulanow
e4c89eb88c REPL: Get rid of passing all previous line classses via constructor parameters 2018-02-07 18:45:36 +03:00
Yan Zhulanow
c3bcb866d1 J2K: Script Codegen (step 2) 2018-02-07 18:45:36 +03:00
Yan Zhulanow
c56480db3e J2K: Script Codegen (step 1) 2018-02-07 18:45:35 +03:00
Yan Zhulanow
3fe91b7eb5 REPL, Refactoring: Small code style fixes 2018-02-07 18:45:34 +03:00
Yan Zhulanow
65919518e0 REPL, Refactoring: A number of trivial renames/moves 2018-02-07 18:45:33 +03:00
Yan Zhulanow
efc2fcf104 REPL, Refactoring, Minor: Lower property visibility 2018-02-07 18:45:32 +03:00
Yan Zhulanow
116edf91ec REPL, Refactoring: Get rid of nasty enum-like String literals 2018-02-07 18:45:31 +03:00
Yan Zhulanow
2b649c4694 REPL, Refactoring: Make ReplFromTerminal and ReplInterpreter not to implement ReplConfiguration 2018-02-07 18:45:30 +03:00
Yan Zhulanow
ad315a1480 REPL, Refactoring: Replace strange callback function with an execution interceptor 2018-02-07 18:45:29 +03:00
Yan Zhulanow
5cadaec72a Minor: Delete an unused class 2018-02-07 18:45:27 +03:00
Yan Zhulanow
5487589455 Minor: Add "repl" to dictionary 2018-02-07 18:45:26 +03:00
Yan Zhulanow
18144c3ed5 REPL, Refactoring: Rename ReplErrorLogger to ReplExceptionReporter
Also rethrow exceptions explicitly.
2018-02-07 18:45:25 +03:00
Yan Zhulanow
37402ef616 REPL: Fix crash on trying to :load with incorrect filename (KT-12037) 2018-02-07 18:45:24 +03:00
45 changed files with 1126 additions and 782 deletions

View File

@@ -5,6 +5,7 @@
<w>impls</w>
<w>kapt</w>
<w>parceler</w>
<w>repl</w>
<w>uast</w>
</words>
</dictionary>

View File

@@ -58,6 +58,16 @@ public class FieldInfo {
return new FieldInfo(owner, fieldType, fieldName, false);
}
@NotNull
public static FieldInfo createForHiddenField(
@NotNull Type owner,
@NotNull Type fieldType,
@NotNull String fieldName,
@NotNull boolean isStatic
) {
return new FieldInfo(owner, fieldType, fieldName, isStatic);
}
private final Type fieldType;
private final Type ownerType;
private final String fieldName;

View File

@@ -1,242 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.context.ScriptContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class ScriptCodegen extends MemberCodegen<KtScript> {
public static ScriptCodegen createScriptCodegen(
@NotNull KtScript declaration,
@NotNull GenerationState state,
@NotNull CodegenContext parentContext
) {
BindingContext bindingContext = state.getBindingContext();
ScriptDescriptor scriptDescriptor = bindingContext.get(BindingContext.SCRIPT, declaration);
assert scriptDescriptor != null;
Type classType = state.getTypeMapper().mapType(scriptDescriptor);
ClassBuilder builder = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(declaration, scriptDescriptor),
classType, declaration.getContainingFile());
List<ScriptDescriptor> earlierScripts = state.getReplSpecific().getEarlierScriptsForReplInterpreter();
ScriptContext scriptContext = parentContext.intoScript(
scriptDescriptor,
earlierScripts == null ? Collections.emptyList() : earlierScripts,
scriptDescriptor,
state.getTypeMapper()
);
return new ScriptCodegen(declaration, state, scriptContext, builder);
}
private final KtScript scriptDeclaration;
private final ScriptContext context;
private final ScriptDescriptor scriptDescriptor;
private final Type classAsmType;
private ScriptCodegen(
@NotNull KtScript scriptDeclaration,
@NotNull GenerationState state,
@NotNull ScriptContext context,
@NotNull ClassBuilder builder
) {
super(state, null, context, scriptDeclaration, builder);
this.scriptDeclaration = scriptDeclaration;
this.context = context;
this.scriptDescriptor = context.getScriptDescriptor();
classAsmType = typeMapper.mapClass(context.getContextDescriptor());
}
@Override
protected void generateDeclaration() {
v.defineClass(scriptDeclaration,
state.getClassFileVersion(),
ACC_PUBLIC | ACC_SUPER,
classAsmType.getInternalName(),
null,
typeMapper.mapSupertype(DescriptorUtilsKt.getSuperClassOrAny(scriptDescriptor).getDefaultType(), null).getInternalName(),
CodegenUtilKt.mapSupertypesNames(typeMapper, DescriptorUtilsKt.getSuperInterfaces(scriptDescriptor), null));
}
@Override
protected void generateBody() {
genMembers();
genFieldsForParameters(v);
genConstructor(scriptDescriptor, v,
context.intoFunction(scriptDescriptor.getUnsubstitutedPrimaryConstructor()));
}
@Override
protected void generateSyntheticPartsBeforeBody() {
generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
}
@Override
protected void generateSyntheticPartsAfterBody() {
}
@Override
protected void generateKotlinMetadataAnnotation() {
generateKotlinClassMetadataAnnotation(scriptDescriptor, true);
}
private void genConstructor(
@NotNull ScriptDescriptor scriptDescriptor,
@NotNull ClassBuilder classBuilder,
@NotNull MethodContext methodContext
) {
JvmMethodSignature jvmSignature = typeMapper.mapScriptSignature(scriptDescriptor, context.getEarlierScripts());
if (state.getReplSpecific().getShouldGenerateScriptResultValue()) {
FieldInfo resultFieldInfo = context.getResultFieldInfo();
classBuilder.newField(
JvmDeclarationOrigin.NO_ORIGIN,
ACC_PUBLIC | ACC_FINAL,
resultFieldInfo.getFieldName(),
resultFieldInfo.getFieldType().getDescriptor(),
null,
null
);
}
MethodVisitor mv = classBuilder.newMethod(
JvmDeclarationOriginKt.OtherOrigin(scriptDeclaration, scriptDescriptor.getUnsubstitutedPrimaryConstructor()),
ACC_PUBLIC, jvmSignature.getAsmMethod().getName(), jvmSignature.getAsmMethod().getDescriptor(),
null, null);
if (state.getClassBuilderMode().generateBodies) {
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
Type classType = typeMapper.mapType(scriptDescriptor);
ClassDescriptor superclass = DescriptorUtilsKt.getSuperClassNotAny(scriptDescriptor);
// TODO: throw if class is not found)
if (superclass == null) {
iv.load(0, classType);
iv.invokespecial("java/lang/Object", "<init>", "()V", false);
}
else {
ConstructorDescriptor ctorDesc = superclass.getUnsubstitutedPrimaryConstructor();
if (ctorDesc == null) throw new RuntimeException("Primary constructor not found for script template " + superclass.toString());
iv.load(0, classType);
int valueParamStart = context.getEarlierScripts().isEmpty() ? 1 : 2; // this + array of earlier scripts if not empty
List<ValueParameterDescriptor> valueParameters = scriptDescriptor.getUnsubstitutedPrimaryConstructor().getValueParameters();
for (ValueParameterDescriptor superclassParam: ctorDesc.getValueParameters()) {
ValueParameterDescriptor valueParam = null;
for (ValueParameterDescriptor vpd: valueParameters) {
if (vpd.getName().equals(superclassParam.getName())) {
valueParam = vpd;
break;
}
}
assert valueParam != null;
iv.load(valueParam.getIndex() + valueParamStart, typeMapper.mapType(valueParam.getType()));
}
CallableMethod ctorMethod = typeMapper.mapToCallableMethod(ctorDesc, false);
String sig = ctorMethod.getAsmMethod().getDescriptor();
iv.invokespecial(
typeMapper.mapSupertype(superclass.getDefaultType(), null).getInternalName(),
"<init>", sig, false);
}
iv.load(0, classType);
FrameMap frameMap = new FrameMap();
frameMap.enterTemp(OBJECT_TYPE);
if (!context.getEarlierScripts().isEmpty()) {
int scriptsParamIndex = frameMap.enterTemp(AsmUtil.getArrayType(OBJECT_TYPE));
int earlierScriptIndex = 0;
for (ScriptDescriptor earlierScript : context.getEarlierScripts()) {
Type earlierClassType = typeMapper.mapClass(earlierScript);
iv.load(0, classType);
iv.load(scriptsParamIndex, earlierClassType);
iv.aconst(earlierScriptIndex++);
iv.aload(OBJECT_TYPE);
iv.checkcast(earlierClassType);
iv.putfield(classType.getInternalName(), context.getScriptFieldName(earlierScript), earlierClassType.getDescriptor());
}
}
ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, methodContext, state, this);
generateInitializers(() -> codegen);
iv.areturn(Type.VOID_TYPE);
}
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void genFieldsForParameters(@NotNull ClassBuilder classBuilder) {
for (ScriptDescriptor earlierScript : context.getEarlierScripts()) {
Type earlierClassName = typeMapper.mapType(earlierScript);
int access = ACC_PUBLIC | ACC_FINAL;
classBuilder.newField(NO_ORIGIN, access, context.getScriptFieldName(earlierScript), earlierClassName.getDescriptor(), null, null);
}
}
private void genMembers() {
for (KtDeclaration declaration : scriptDeclaration.getDeclarations()) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
genSimpleMember(declaration);
}
else if (declaration instanceof KtClassOrObject) {
genClassOrObject((KtClassOrObject) declaration);
}
else if (declaration instanceof KtDestructuringDeclaration) {
for (KtDestructuringDeclarationEntry entry : ((KtDestructuringDeclaration) declaration).getEntries()) {
genSimpleMember(entry);
}
}
}
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.context.ScriptContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
import org.jetbrains.org.objectweb.asm.Opcodes.*
class ScriptCodegen private constructor(
private val scriptDeclaration: KtScript,
state: GenerationState,
private val scriptContext: ScriptContext,
builder: ClassBuilder
) : MemberCodegen<KtScript>(state, null, scriptContext, scriptDeclaration, builder) {
private val scriptDescriptor = scriptContext.scriptDescriptor
private val classAsmType = typeMapper.mapClass(scriptContext.contextDescriptor)
override fun generateDeclaration() {
v.defineClass(
scriptDeclaration,
state.classFileVersion,
ACC_PUBLIC or ACC_SUPER,
classAsmType.internalName,
null,
typeMapper.mapSupertype(scriptDescriptor.getSuperClassOrAny().defaultType, null).internalName,
mapSupertypesNames(typeMapper, scriptDescriptor.getSuperInterfaces(), null)
)
}
override fun generateBody() {
genMembers()
genFieldsForParameters(v)
genConstructor(scriptDescriptor, v, scriptContext.intoFunction(scriptDescriptor.unsubstitutedPrimaryConstructor))
}
override fun generateSyntheticPartsBeforeBody() {
generatePropertyMetadataArrayFieldIfNeeded(classAsmType)
}
override fun generateSyntheticPartsAfterBody() {}
override fun generateKotlinMetadataAnnotation() {
generateKotlinClassMetadataAnnotation(scriptDescriptor, true)
}
private fun genConstructor(
scriptDescriptor: ScriptDescriptor,
classBuilder: ClassBuilder,
methodContext: MethodContext
) {
val jvmSignature = typeMapper.mapScriptSignature(scriptDescriptor, scriptContext.earlierScripts)
if (state.replSpecific.shouldGenerateScriptResultValue) {
val resultFieldInfo = scriptContext.resultFieldInfo
classBuilder.newField(
JvmDeclarationOrigin.NO_ORIGIN,
ACC_PUBLIC or ACC_FINAL,
resultFieldInfo.fieldName,
resultFieldInfo.fieldType.descriptor,
null, null)
}
val mv = classBuilder.newMethod(
OtherOrigin(scriptDeclaration, scriptDescriptor.unsubstitutedPrimaryConstructor),
ACC_PUBLIC, jvmSignature.asmMethod.name, jvmSignature.asmMethod.descriptor, null, null)
if (state.classBuilderMode.generateBodies) {
mv.visitCode()
val iv = InstructionAdapter(mv)
val classType = typeMapper.mapType(scriptDescriptor)
val superclass = scriptDescriptor.getSuperClassNotAny()
// TODO: throw if class is not found)
if (superclass == null) {
iv.load(0, classType)
iv.invokespecial("java/lang/Object", "<init>", "()V", false)
}
else {
val ctorDesc = superclass.unsubstitutedPrimaryConstructor
?: throw RuntimeException("Primary constructor not found for script template " + superclass.toString())
iv.load(0, classType)
val valueParamStart = if (scriptContext.earlierScripts.isEmpty()) 1 else 2 // this + array of earlier scripts if not empty
val valueParameters = scriptDescriptor.unsubstitutedPrimaryConstructor.valueParameters
for (superclassParam in ctorDesc.valueParameters) {
val valueParam = valueParameters.first { it.name == superclassParam.name }
iv.load(valueParam!!.index + valueParamStart, typeMapper.mapType(valueParam.type))
}
val ctorMethod = typeMapper.mapToCallableMethod(ctorDesc, false)
val sig = ctorMethod.getAsmMethod().descriptor
iv.invokespecial(
typeMapper.mapSupertype(superclass.defaultType, null).internalName,
"<init>", sig, false)
}
iv.load(0, classType)
val frameMap = FrameMap()
frameMap.enterTemp(OBJECT_TYPE)
if (!scriptContext.earlierScripts.isEmpty()) {
val scriptsParamIndex = frameMap.enterTemp(AsmUtil.getArrayType(OBJECT_TYPE))
var earlierScriptIndex = 0
for (earlierScript in scriptContext.earlierScripts) {
val earlierClassType = typeMapper.mapClass(earlierScript)
iv.load(0, classType)
iv.load(scriptsParamIndex, earlierClassType)
iv.aconst(earlierScriptIndex++)
iv.aload(OBJECT_TYPE)
iv.checkcast(earlierClassType)
iv.putfield(classType.internalName, scriptContext.getScriptFieldName(earlierScript), earlierClassType.descriptor)
}
}
val codegen = ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, methodContext, state, this)
generateInitializers { codegen }
iv.areturn(Type.VOID_TYPE)
}
mv.visitMaxs(-1, -1)
mv.visitEnd()
}
private fun genFieldsForParameters(classBuilder: ClassBuilder) {
for (earlierScript in scriptContext.earlierScripts) {
val earlierClassName = typeMapper.mapType(earlierScript)
val access = ACC_PUBLIC or ACC_FINAL
classBuilder.newField(NO_ORIGIN, access, scriptContext.getScriptFieldName(earlierScript), earlierClassName.descriptor, null, null)
}
}
private fun genMembers() {
for (declaration in scriptDeclaration.declarations) {
if (declaration is KtProperty || declaration is KtNamedFunction || declaration is KtTypeAlias) {
genSimpleMember(declaration)
}
else if (declaration is KtClassOrObject) {
genClassOrObject(declaration)
}
else if (declaration is KtDestructuringDeclaration) {
for (entry in declaration.entries) {
genSimpleMember(entry)
}
}
}
}
companion object {
@JvmStatic
fun createScriptCodegen(
declaration: KtScript,
state: GenerationState,
parentContext: CodegenContext<*>
): MemberCodegen<KtScript> {
val bindingContext = state.bindingContext
val scriptDescriptor = bindingContext.get<PsiElement, ScriptDescriptor>(BindingContext.SCRIPT, declaration)!!
if (scriptDescriptor.isReplSnippet) {
return ScriptCodegenForRepl.createScriptCodegen(declaration, state, parentContext)
}
val classType = state.typeMapper.mapType(scriptDescriptor)
val builder = state.factory.newVisitor(
OtherOrigin(declaration, scriptDescriptor), classType, declaration.containingFile)
val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
val scriptContext = parentContext.intoScript(
scriptDescriptor,
earlierScripts ?: emptyList(),
scriptDescriptor,
state.typeMapper
)
return ScriptCodegen(declaration, state, scriptContext, builder)
}
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.context.ScriptContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.Opcodes.*
class ScriptCodegenForRepl private constructor(
private val scriptDeclaration: KtScript,
state: GenerationState,
private val scriptContext: ScriptContext,
builder: ClassBuilder
) : MemberCodegen<KtScript>(state, null, scriptContext, scriptDeclaration, builder) {
private val scriptDescriptor = scriptContext.scriptDescriptor
private val classAsmType = typeMapper.mapClass(scriptContext.contextDescriptor)
override fun generateDeclaration() {
// Do not allow superclasses for REPL line scripts
assert(scriptDescriptor.getSuperClassNotAny() == null)
v.defineClass(
scriptDeclaration,
state.classFileVersion,
ACC_PUBLIC or ACC_SUPER,
classAsmType.internalName,
null,
"java/lang/Object",
mapSupertypesNames(typeMapper, scriptDescriptor.getSuperInterfaces(), null)
)
}
override fun generateBody() {
genMembers()
genDefaultConstructor(v)
genRunMethod(v, scriptContext.intoFunction(scriptDescriptor.unsubstitutedPrimaryConstructor))
genResultFieldIfNeeded(v)
}
override fun generateSyntheticPartsBeforeBody() {
generatePropertyMetadataArrayFieldIfNeeded(classAsmType)
}
override fun generateSyntheticPartsAfterBody() {}
override fun generateKotlinMetadataAnnotation() {
generateKotlinClassMetadataAnnotation(scriptDescriptor, true)
}
private fun genResultFieldIfNeeded(classBuilder: ClassBuilder) {
if (state.replSpecific.shouldGenerateScriptResultValue) {
val resultFieldInfo = scriptContext.resultFieldInfo
classBuilder.newField(
JvmDeclarationOrigin.NO_ORIGIN,
ACC_PUBLIC or ACC_FINAL or ACC_STATIC,
resultFieldInfo.fieldName,
resultFieldInfo.fieldType.descriptor,
null, null)
}
}
private fun genDefaultConstructor(classBuilder: ClassBuilder) {
val mv = classBuilder.newMethod(OtherOrigin(scriptDeclaration), ACC_PUBLIC, "<init>", "()V", null, null)
if (state.classBuilderMode.generateBodies) {
mv.visitCode()
with (InstructionAdapter(mv)) {
load(0, typeMapper.mapType(scriptDescriptor))
invokespecial("java/lang/Object", "<init>", "()V", false)
areturn(Type.VOID_TYPE)
}
}
mv.visitMaxs(-1, -1)
mv.visitEnd()
}
private fun genRunMethod(classBuilder: ClassBuilder, methodContext: MethodContext) {
val mv = classBuilder.newMethod(OtherOrigin(scriptDeclaration), ACC_PUBLIC or ACC_STATIC, RUN_METHOD_NAME, "()V", null, null)
if (state.classBuilderMode.generateBodies) {
mv.visitCode()
with (InstructionAdapter(mv)) {
val codegen = ExpressionCodegen(mv, FrameMap(), Type.VOID_TYPE, methodContext, state, this@ScriptCodegenForRepl)
generateInitializers { codegen }
areturn(Type.VOID_TYPE)
}
}
mv.visitMaxs(-1, -1)
mv.visitEnd()
}
private fun genMembers() {
for (declaration in scriptDeclaration.declarations) {
if (declaration is KtProperty || declaration is KtNamedFunction || declaration is KtTypeAlias) {
genSimpleMember(declaration)
}
else if (declaration is KtClassOrObject) {
genClassOrObject(declaration)
}
else if (declaration is KtDestructuringDeclaration) {
for (entry in declaration.entries) {
genSimpleMember(entry)
}
}
}
}
companion object {
val RUN_METHOD_NAME = "run"
@JvmStatic
fun createScriptCodegen(
declaration: KtScript,
state: GenerationState,
parentContext: CodegenContext<*>
): MemberCodegen<KtScript> {
val bindingContext = state.bindingContext
val scriptDescriptor = bindingContext.get<PsiElement, ScriptDescriptor>(BindingContext.SCRIPT, declaration)!!
val classType = state.typeMapper.mapType(scriptDescriptor)
val builder = state.factory.newVisitor(
OtherOrigin(declaration, scriptDescriptor), classType, declaration.containingFile)
val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
val scriptContext = parentContext.intoScript(
scriptDescriptor,
earlierScripts ?: emptyList(),
scriptDescriptor,
state.typeMapper
)
return ScriptCodegenForRepl(declaration, state, scriptContext, builder)
}
}
}

View File

@@ -69,9 +69,15 @@ public class ScriptContext extends ClassContext {
public FieldInfo getResultFieldInfo() {
assert getState().getReplSpecific().getShouldGenerateScriptResultValue() : "Should not be called unless 'scriptResultFieldName' is set";
GenerationState state = getState();
String scriptResultFieldName = state.getReplSpecific().getScriptResultFieldName();
assert scriptResultFieldName != null;
return FieldInfo.createForHiddenField(state.getTypeMapper().mapClass(scriptDescriptor), AsmTypes.OBJECT_TYPE, scriptResultFieldName);
return FieldInfo.createForHiddenField(
state.getTypeMapper().mapClass(scriptDescriptor),
AsmTypes.OBJECT_TYPE,
scriptResultFieldName,
scriptDescriptor.isReplSnippet());
}
@NotNull

View File

@@ -17,24 +17,18 @@
package org.jetbrains.kotlin.cli.common.repl
import java.io.File
import java.net.URLClassLoader
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
open class GenericReplEvaluatorState(baseClasspath: Iterable<File>, baseClassloader: ClassLoader?, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
: IReplStageState<EvalClassWithInstanceAndLoader>
{
open class GenericReplEvaluatorState(
val classLoader: ReplClassLoader,
final override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
) : IReplStageState<EvalClassWithInstanceAndLoader> {
override val history: IReplStageHistory<EvalClassWithInstanceAndLoader> = BasicReplStageHistory(lock)
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
val currentClasspath: List<File> get() = lock.read {
history.peek()?.item?.classLoader?.listAllUrlsAsFiles()
?: topClassLoader.listAllUrlsAsFiles()
history.peek()?.item?.classLoader?.listAllUrlsAsFiles() ?: classLoader.listAllUrlsAsFiles()
}
}
internal fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
}

View File

@@ -21,16 +21,23 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
baseClasspath: Iterable<File>,
baseClasspath: List<File>,
baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
private val fallbackScriptArgs: ScriptArgsWithTypes? = null,
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
) : ReplFullEvaluator {
private val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode)
override val classLoader: ReplClassLoader
get() = evaluator.classLoader
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = AggregatedReplStageState(compiler.createState(lock), evaluator.createState(lock), lock)
override fun compileAndEval(state: IReplStageState<*>, codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
if (codeLine.code.trim().isEmpty()) {
return ReplEvalResult.UnitResult()
}
return state.lock.write {
val aggregatedState = state.asState(AggregatedReplStageState::class.java)
val compiled = compiler.compile(state, codeLine)

View File

@@ -23,13 +23,14 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
open class GenericReplEvaluator(
val baseClasspath: Iterable<File>,
val baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
baseClasspath: List<File>,
baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
) : ReplEvaluator {
override val classLoader = ReplClassLoader(baseClasspath, baseClassloader)
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = GenericReplEvaluatorState(baseClasspath, baseClassloader, lock)
override fun createState(lock: ReentrantReadWriteLock) = GenericReplEvaluatorState(classLoader, lock)
override fun eval(state: IReplStageState<*>,
compileResult: ReplCompileResult.CompiledClasses,
@@ -64,49 +65,46 @@ open class GenericReplEvaluator(
return@eval ReplEvalResult.HistoryMismatch(firstMismatch.first?.id?.no ?: firstMismatch.second?.no ?: -1 /* means error? */)
}
val (classLoader, scriptClass) = try {
val scriptClass = try {
historyActor.processClasses(compileResult)
}
catch (e: Exception) {
return@eval ReplEvalResult.Error.Runtime(e.message ?: "unknown", e)
}
val currentScriptArgs = scriptArgs ?: fallbackScriptArgs
val useScriptArgs = currentScriptArgs?.scriptArgs
val useScriptArgsTypes = currentScriptArgs?.scriptArgsTypes?.map { it.java }
historyActor.addPlaceholder(
compileResult.lineId,
EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, evalState.classLoader, invokeWrapper))
val hasHistory = historyActor.effectiveHistory.isNotEmpty()
val constructorParams: Array<Class<*>> = (if (hasHistory) arrayOf<Class<*>>(Array<Any>::class.java) else emptyArray<Class<*>>()) +
(useScriptArgs?.mapIndexed { i, it -> useScriptArgsTypes?.getOrNull(i) ?: it?.javaClass ?: Any::class.java } ?: emptyList())
val constructorArgs: Array<out Any?> = if (hasHistory) arrayOf(historyActor.effectiveHistory.map { it.instance }.takeIf { it.isNotEmpty() }?.toTypedArray(),
*(useScriptArgs.orEmpty()))
else useScriptArgs.orEmpty()
// TODO: try/catch ?
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
historyActor.addPlaceholder(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, classLoader, invokeWrapper))
fun makeErrorMessage(e: Throwable) = renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.run")
val scriptInstance =
try {
if (invokeWrapper != null) invokeWrapper.invoke { scriptInstanceConstructor.newInstance(*constructorArgs) }
else scriptInstanceConstructor.newInstance(*constructorArgs)
val scriptInstance = if (invokeWrapper != null) {
invokeWrapper.invoke { scriptClass.newInstance() }
}
else {
scriptClass.newInstance()
}
scriptInstance.javaClass.getDeclaredMethod("run").invoke(null)
scriptInstance
}
catch (e: InvocationTargetException) {
// ignore everything in the stack trace until this constructor call
return@eval ReplEvalResult.Error.Runtime(renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e.targetException as? Exception)
return@eval ReplEvalResult.Error.Runtime(makeErrorMessage(e), e.targetException as? Exception)
}
catch (e: Throwable) {
// ignore everything in the stack trace until this constructor call
return@eval ReplEvalResult.Error.Runtime(renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
return@eval ReplEvalResult.Error.Runtime(makeErrorMessage(e), e as? Exception)
}
finally {
historyActor.removePlaceholder(compileResult.lineId)
}
historyActor.addFinal(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
historyActor.addFinal(
compileResult.lineId,
EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, evalState.classLoader, invokeWrapper))
val resultField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
val resultValue: Any? = resultField.get(scriptInstance)
@@ -123,43 +121,39 @@ open class GenericReplEvaluator(
}
private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorState) {
open val effectiveHistory: List<EvalClassWithInstanceAndLoader>
get() = state.history.map { it.item }
open val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.map { it.item }
open fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? {
return state.history.firstMismatch(other)
}
open fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? = state.history.firstMismatch(other)
open fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
open fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {
state.history.push(lineId, value)
}
open fun removePlaceholder(lineId: ILineId): Boolean = state.history.verifiedPop(lineId) != null
open fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
open fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {
state.history.push(lineId, value)
}
open fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> = prependClassLoaderWithNewClasses(effectiveHistory, compileResult)
open fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Class<out Any> {
class ClassToLoad(val name: JvmClassName, val bytes: ByteArray)
private fun prependClassLoaderWithNewClasses(effectiveHistory: List<EvalClassWithInstanceAndLoader>,
compileResult: ReplCompileResult.CompiledClasses
): Pair<ClassLoader, Class<out Any>> {
var mainLineClassName: String? = null
val classLoader = makeReplClassLoader(effectiveHistory.lastOrNull()?.classLoader ?: state.topClassLoader, compileResult.classpathAddendum)
fun classNameFromPath(path: String) = JvmClassName.byInternalName(path.removeSuffix(".class"))
fun compiledClassesNames() = compileResult.classes.map { classNameFromPath(it.path).internalName.replace('/', '.') }
val expectedClassName = compileResult.mainClassName
compileResult.classes.filter { it.path.endsWith(".class") }
.forEach {
val className = classNameFromPath(it.path)
if (className.internalName == expectedClassName || className.internalName.endsWith("/$expectedClassName")) {
mainLineClassName = className.internalName.replace('/', '.')
}
classLoader.addClass(className, it.bytes)
}
val classesToLoad = compileResult.classes
.filter { it.path.endsWith(".class") }
.map { ClassToLoad(JvmClassName.byInternalName(it.path.removeSuffix(".class")), it.bytes) }
val scriptClass = try {
classLoader.loadClass(mainLineClassName!!)
val mainClassName = compileResult.mainClassName
if (!classesToLoad.any { it.name.internalName == mainClassName }) {
val compiledClassNames = classesToLoad.joinToString { it.name.internalName }
throw IllegalStateException("Error loading class $mainClassName: known classes: $compiledClassNames")
}
catch (t: Throwable) {
throw Exception("Error loading class $mainLineClassName: known classes: ${compiledClassesNames()}", t)
}
return Pair(classLoader, scriptClass)
classesToLoad.forEach { state.classLoader.addClass(it.name, it.bytes) }
return Class.forName(mainClassName, true, state.classLoader)
}
}
@@ -181,8 +175,7 @@ private open class HistoryActionsForRepeatRecentOnly(state: GenericReplEvaluator
state.history.push(lineId, value)
}
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> =
currentLast.item.classLoader to currentLast.item.klass.java
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses) = currentLast.item.klass.java
}
private open class HistoryActionsForRepeatAny(state: GenericReplEvaluatorState, val matchingLine: ReplHistoryRecord<EvalClassWithInstanceAndLoader>): HistoryActionsForNoRepeat(state) {
@@ -206,6 +199,5 @@ private open class HistoryActionsForRepeatAny(state: GenericReplEvaluatorState,
}
}
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> =
matchingLine.item.classLoader to matchingLine.item.klass.java
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses) = matchingLine.item.klass.java
}

View File

@@ -157,7 +157,9 @@ sealed class ReplEvalResult : Serializable {
}
}
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction {
val classLoader: ReplClassLoader
}
// --- compileAdnEval

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.common.repl;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintWriter;
import java.util.Map;
public class ReplClassLoader extends ClassLoader {
private final Map<JvmClassName, byte[]> classes = Maps.newLinkedHashMap();
public ReplClassLoader(@NotNull ClassLoader parent) {
super(parent);
}
@NotNull
@Override
protected Class<?> findClass(@NotNull String name) throws ClassNotFoundException {
byte[] classBytes = classes.get(JvmClassName.byFqNameWithoutInnerClasses(name));
if (classBytes != null) {
return defineClass(name, classBytes, 0, classBytes.length);
}
else {
return super.findClass(name);
}
}
public void addClass(@NotNull JvmClassName className, @NotNull byte[] bytes) {
byte[] oldBytes = classes.put(className, bytes);
if (oldBytes != null) {
throw new IllegalStateException("Rewrite at key " + className);
}
}
public void dumpClasses(@NotNull PrintWriter writer) {
for (byte[] classBytes : classes.values()) {
new ClassReader(classBytes).accept(new TraceClassVisitor(writer), 0);
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.common.repl
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.util.TraceClassVisitor
import java.io.ByteArrayInputStream
import java.io.File
import java.io.PrintWriter
import java.net.URL
import java.net.URLClassLoader
import java.net.URLConnection
import java.net.URLStreamHandler
import java.util.*
class ReplClassLoader(
files: List<File>,
parent: ClassLoader?
) : URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray(), parent) {
private val classes = LinkedHashMap<JvmClassName, ByteArray>()
fun addJar(file: File) {
super.addURL(file.toURI().toURL())
}
override fun findClass(name: String): Class<*> {
val classBytes = classes[JvmClassName.byFqNameWithoutInnerClasses(name)]
return if (classBytes != null) {
defineClass(name, classBytes, 0, classBytes.size)
}
else {
super.findClass(name)
}
}
override fun findResource(name: String): URL {
val jvmName = JvmClassName.byFqNameWithoutInnerClasses(name)
classes[jvmName]?.let { return wrapToUrl(jvmName.internalName + ".class", it) }
return super.findResource(name)
}
override fun findResources(name: String): Enumeration<URL> {
val result = mutableListOf<URL>()
// Add our custom resource
val jvmName = JvmClassName.byFqNameWithoutInnerClasses(name)
classes[jvmName]?.let { wrapToUrl(jvmName.internalName + ".class", it) }?.let { result += it }
// Add other resources
super.findResources(name).asSequence().forEach { result += it }
return Collections.enumeration(result)
}
private fun wrapToUrl(path: String, bytes: ByteArray): URL {
return URL("repl", null, 0, path, object : URLStreamHandler() {
override fun openConnection(u: URL) = object : URLConnection(u) {
override fun connect() {}
override fun getInputStream() = ByteArrayInputStream(bytes)
}
})
}
fun addClass(className: JvmClassName, bytes: ByteArray) {
classes.put(className, bytes)?.let {
throw IllegalStateException("Rewrite at key " + className)
}
}
fun dumpClasses(writer: PrintWriter) {
for (classBytes in classes.values) {
ClassReader(classBytes).accept(TraceClassVisitor(writer), 0)
}
}
}

View File

@@ -20,16 +20,14 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
interface ILineId : Comparable<ILineId> {
val no: Int
val generation: Int
}
data class ReplHistoryRecord<out T> (val id: ILineId, val item: T)
data class ReplHistoryRecord<out T>(val id: ILineId, val item: T)
interface IReplStageHistory<T> : List<ReplHistoryRecord<T>> {
fun peek(): ReplHistoryRecord<T>? = lock.read { lastOrNull() }
fun push(id: ILineId, item: T)
@@ -37,8 +35,12 @@ interface IReplStageHistory<T> : List<ReplHistoryRecord<T>> {
fun pop(): ReplHistoryRecord<T>?
fun verifiedPop(id: ILineId): ReplHistoryRecord<T>? = lock.write {
if (lastOrNull()?.id == id) pop()
else null
if (lastOrNull()?.id == id) {
pop()
}
else {
null
}
}
fun reset(): Iterable<ILineId>
@@ -55,26 +57,49 @@ interface IReplStageState<T> {
val currentGeneration: Int
fun getNextLineNo(): Int = history.peek()?.id?.no?.let { it + 1 } ?: REPL_CODE_LINE_FIRST_NO // TODO: it should be more robust downstream (e.g. use atomic)
fun getNextLineNo(): Int {
// TODO: it should be more robust downstream (e.g. use atomic)
return history.peek()?.id?.no?.let { it + 1 } ?: REPL_CODE_LINE_FIRST_NO
}
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
if (target.isAssignableFrom(this::class.java)) this as StateT
else throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT {
return if (target.isAssignableFrom(this::class.java)) {
@Suppress("UNCHECKED_CAST")
this as StateT
}
else {
throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
}
}
}
fun <T> IReplStageHistory<T>.firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<T>?, ILineId?>? =
lock.read {
iterator().asSequence().zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
fun <T> IReplStageHistory<T>.firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<T>?, ILineId?>? = lock.read {
iterator().asSequence()
.zip(other.asSequence())
.firstOrNull { it.first.id != it.second }
?.let { it.first to it.second }
}
fun<T> IReplStageHistory<T>.firstMismatchFiltered(other: Sequence<ILineId>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ILineId?>? =
lock.read {
iterator().asSequence().filter(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
fun<T> IReplStageHistory<T>.firstMismatchFiltered(
other: Sequence<ILineId>,
predicate: (ReplHistoryRecord<T>) -> Boolean
): Pair<ReplHistoryRecord<T>?, ILineId?>? = lock.read {
iterator().asSequence()
.filter(predicate)
.zip(other.asSequence())
.firstOrNull { it.first.id != it.second }
?.let { it.first to it.second }
}
fun<T> IReplStageHistory<T>.firstMismatchWhile(other: Sequence<ILineId>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ILineId?>? =
fun<T> IReplStageHistory<T>.firstMismatchWhile(
other: Sequence<ILineId>,
predicate: (ReplHistoryRecord<T>) -> Boolean
): Pair<ReplHistoryRecord<T>?, ILineId?>? =
lock.read {
iterator().asSequence().takeWhile(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
}
iterator().asSequence()
.takeWhile(predicate)
.zip(other.asSequence())
.firstOrNull { it.first.id != it.second }
?.let { it.first to it.second }
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
public class EarlierLine {
@NotNull
private final String code;
@NotNull
private final ScriptDescriptor scriptDescriptor;
@NotNull
private final Class<?> scriptClass;
@NotNull
private final Object scriptInstance;
public EarlierLine(
@NotNull String code,
@NotNull ScriptDescriptor scriptDescriptor,
@NotNull Class<?> scriptClass,
@NotNull Object scriptInstance
) {
this.code = code;
this.scriptDescriptor = scriptDescriptor;
this.scriptClass = scriptClass;
this.scriptInstance = scriptInstance;
}
@NotNull
public String getCode() {
return code;
}
@NotNull
public ScriptDescriptor getScriptDescriptor() {
return scriptDescriptor;
}
@NotNull
public Class<?> getScriptClass() {
return scriptClass;
}
@NotNull
public Object getScriptInstance() {
return scriptInstance;
}
}

View File

@@ -62,11 +62,14 @@ abstract class GenericReplCheckerState: IReplStageState<ScriptDescriptor> {
var lastLineState: LineState? = null // for transferring state to the compiler in most typical case
}
class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<ScriptDescriptor>, GenericReplCheckerState() {
class GenericReplCompilerState(
environment: KotlinCoreEnvironment,
override val lock: ReentrantReadWriteLock
) : IReplStageState<ScriptDescriptor>, GenericReplCheckerState() {
override val history = ReplCompilerStageHistory(this)
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
override val currentGeneration: Int
get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
val analyzerEngine = ReplCodeAnalyzer(environment)

View File

@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplTerminalDiagnosticMessageHolder
import org.jetbrains.kotlin.cli.jvm.repl.messages.ConsoleDiagnosticMessageHolder
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
@@ -43,8 +43,8 @@ const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
open class GenericReplChecker(
disposable: Disposable,
val scriptDefinition: KotlinScriptDefinition,
val compilerConfiguration: CompilerConfiguration,
private val scriptDefinition: KotlinScriptDefinition,
private val compilerConfiguration: CompilerConfiguration,
messageCollector: MessageCollector
) : ReplCheckAction {
@@ -65,7 +65,7 @@ open class GenericReplChecker(
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
internal fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
private fun createDiagnosticHolder() = ConsoleDiagnosticMessageHolder()
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
state.lock.write {
@@ -88,7 +88,7 @@ open class GenericReplChecker(
return when {
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete()
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics)
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderMessage())
else -> ReplCheckResult.Ok()
}
}

View File

@@ -35,15 +35,20 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.utils.repl.IS_REPL_SNIPPET
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.util.TraceClassVisitor
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintWriter
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
// WARNING: not thread safe, assuming external synchronization
open class GenericReplCompiler(disposable: Disposable,
protected val scriptDefinition: KotlinScriptDefinition,
protected val compilerConfiguration: CompilerConfiguration,
scriptDefinition: KotlinScriptDefinition,
private val compilerConfiguration: CompilerConfiguration,
messageCollector: MessageCollector
) : ReplCompiler {
@@ -79,10 +84,14 @@ open class GenericReplCompiler(disposable: Disposable,
classpathAddendum = newDependencies?.let { checker.environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
}
val ktScript = psiFile.getChildOfType<KtScript>()?.also { ktScript ->
ktScript.putUserData(IS_REPL_SNIPPET, true)
}
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
val scriptDescriptor = when (analysisResult) {
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderedDiagnostics)
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderMessage())
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
else -> error("Unexpected result ${analysisResult::class.java}")
}
@@ -107,7 +116,7 @@ open class GenericReplCompiler(disposable: Disposable,
val generatedClassname = makeScriptBaseName(codeLine)
compilerState.history.push(LineId(codeLine), scriptDescriptor)
val expression = psiFile.getChildOfType<KtScript>()?.
val expression = ktScript?.
getChildOfType<KtBlockExpression>()?.
getChildOfType<KtScriptInitializer>()?.
getChildOfType<KtExpression>()

View File

@@ -41,10 +41,11 @@ import org.jetbrains.kotlin.resolve.lazy.*
import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo
import org.jetbrains.kotlin.resolve.lazy.declarations.*
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
@@ -217,12 +218,23 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
}
private fun computeFileScopes(lineInfo: LineInfo, fileScopeFactory: FileScopeFactory): FileScopes? {
// create scope that wraps previous line lexical scope and adds imports from this line
val lexicalScopeAfterLastLine = lineInfo.parentLine?.lineDescriptor?.scopeForInitializerResolution ?: return null
val lastLineImports = lexicalScopeAfterLastLine.parentsWithSelf.first { it is ImportingScope } as ImportingScope
val previousLineDescriptor = lineInfo.parentLine?.lineDescriptor ?: return null
val lexicalScopeAfterLastLine = previousLineDescriptor.scopeForInitializerResolution
val lastLineImports = lexicalScopeAfterLastLine.parentsWithSelf.firstIsInstance<ImportingScope>()
val scopesForThisLine = fileScopeFactory.createScopesForFile(lineInfo.linePsi, lastLineImports)
val combinedLexicalScopes = lexicalScopeAfterLastLine.replaceImportingScopes(scopesForThisLine.importingScope)
return FileScopes(combinedLexicalScopes, scopesForThisLine.importingScope, scopesForThisLine.importResolver)
// Create scope that wraps previous line lexical scope and adds imports from this line
val newScope = LexicalChainedScope(
lexicalScopeAfterLastLine.replaceImportingScopes(scopesForThisLine.importingScope),
previousLineDescriptor,
false,
null,
LexicalScopeKind.CODE_BLOCK,
listOf(previousLineDescriptor.unsubstitutedMemberScope))
return FileScopes(newScope, scopesForThisLine.importingScope, scopesForThisLine.importResolver)
}
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl
import org.jetbrains.kotlin.cli.jvm.repl.messages.*
import org.jetbrains.kotlin.cli.jvm.repl.reader.ConsoleReplCommandReader
import org.jetbrains.kotlin.cli.jvm.repl.reader.IdeReplCommandReader
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandReader
import java.io.PrintWriter
import java.io.StringWriter
interface ReplConfiguration {
val writer: ReplWriter
val errorLogger: ReplErrorLogger
val commandReader: ReplCommandReader
val allowIncompleteLines: Boolean
fun createDiagnosticHolder(): DiagnosticMessageHolder
fun onUserCodeExecuting(isExecuting: Boolean)
}
class ReplForIdeConfiguration : ReplConfiguration {
override val allowIncompleteLines: Boolean
get() = false
override fun onUserCodeExecuting(isExecuting: Boolean) {
sinWrapper.isReplScriptExecuting = isExecuting
}
override fun createDiagnosticHolder() = ReplIdeDiagnosticMessageHolder()
override val writer: ReplWriter
override val errorLogger: ReplErrorLogger
override val commandReader: ReplCommandReader
val sinWrapper: ReplSystemInWrapper
init {
// wrapper for `out` is required to escape every input in [ideMode];
// if [ideMode == false] then just redirects all input to [System.out]
// if user calls [System.setOut(...)] then undefined behaviour
val soutWrapper = ReplSystemOutWrapperForIde(System.out)
System.setOut(soutWrapper)
// wrapper for `in` is required to give user possibility of calling
// [readLine] from ide-console repl
sinWrapper = ReplSystemInWrapper(System.`in`, soutWrapper)
System.setIn(sinWrapper)
writer = soutWrapper
errorLogger = IdeReplErrorLogger(writer)
commandReader = IdeReplCommandReader()
}
}
class ConsoleReplConfiguration : ReplConfiguration {
override val allowIncompleteLines: Boolean
get() = true
override fun onUserCodeExecuting(isExecuting: Boolean) {
// do nothing
}
override fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
override val writer: ReplWriter
override val errorLogger: ReplErrorLogger
override val commandReader: ReplCommandReader
init {
writer = ReplConsoleWriter()
errorLogger = object : ReplErrorLogger {
override fun logException(e: Throwable): Nothing {
throw e
}
}
commandReader = ConsoleReplCommandReader()
}
}
interface ReplErrorLogger {
fun logException(e: Throwable): Nothing
}
class IdeReplErrorLogger(private val replWriter: ReplWriter) : ReplErrorLogger {
override fun logException(e: Throwable): Nothing {
val errorStringWriter = StringWriter()
val errorPrintWriter = PrintWriter(errorStringWriter)
e.printStackTrace(errorPrintWriter)
val writerString = errorStringWriter.toString()
val internalErrorText = if (writerString.isEmpty()) "Unknown error" else writerString
replWriter.sendInternalErrorReport(internalErrorText)
throw e
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl
import org.jetbrains.kotlin.cli.jvm.repl.writer.ReplWriter
import java.io.PrintWriter
import java.io.StringWriter
interface ReplExceptionReporter {
fun report(e: Throwable)
companion object DoNothing : ReplExceptionReporter {
override fun report(e: Throwable) {}
}
}
class IdeReplExceptionReporter(private val replWriter: ReplWriter) : ReplExceptionReporter {
override fun report(e: Throwable) {
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter)
e.printStackTrace(printWriter)
val writerString = stringWriter.toString()
val internalErrorText = if (writerString.isEmpty()) "Unknown error" else writerString
replWriter.sendInternalErrorReport(internalErrorText)
}
}

View File

@@ -20,12 +20,15 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.cli.jvm.repl.configuration.ConsoleReplConfiguration
import org.jetbrains.kotlin.cli.jvm.repl.configuration.ReplConfiguration
import org.jetbrains.kotlin.cli.jvm.repl.configuration.IdeReplConfiguration
import org.jetbrains.kotlin.cli.jvm.repl.messages.unescapeLineBreaks
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import java.io.File
import java.io.IOException
import java.io.PrintWriter
import java.util.*
import java.util.concurrent.Callable
@@ -35,8 +38,8 @@ import java.util.concurrent.Future
class ReplFromTerminal(
disposable: Disposable,
compilerConfiguration: CompilerConfiguration,
replConfiguration: ReplConfiguration
) : ReplConfiguration by replConfiguration {
private val replConfiguration: ReplConfiguration
) {
private val replInitializer: Future<ReplInterpreter> = Executors.newSingleThreadExecutor().submit(Callable {
ReplInterpreter(disposable, compilerConfiguration, replConfiguration)
})
@@ -44,13 +47,17 @@ class ReplFromTerminal(
private val replInterpreter: ReplInterpreter
get() = replInitializer.get()
private val messageCollector: MessageCollector = compilerConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
private val writer get() = replConfiguration.writer
private val messageCollector = compilerConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
private fun doRun() {
try {
writer.printlnWelcomeMessage("Welcome to Kotlin version ${KotlinCompilerVersion.VERSION} " +
"(JRE ${System.getProperty("java.runtime.version")})")
writer.printlnWelcomeMessage("Type :help for help, :quit for quit")
with (writer) {
printlnWelcomeMessage("Welcome to Kotlin version ${KotlinCompilerVersion.VERSION} " +
"(JRE ${System.getProperty("java.runtime.version")})")
printlnWelcomeMessage("Type :help for help, :quit for quit")
}
// Display compiler messages related to configuration and CLI arguments, quit if there are errors
val hasErrors = messageCollector.hasErrors()
@@ -66,14 +73,16 @@ class ReplFromTerminal(
}
}
catch (e: Exception) {
errorLogger.logException(e)
replConfiguration.exceptionReporter.report(e)
throw e
}
finally {
try {
commandReader.flushHistory()
replConfiguration.commandReader.flushHistory()
}
catch (e: Exception) {
errorLogger.logException(e)
replConfiguration.exceptionReporter.report(e)
throw e
}
}
@@ -86,7 +95,7 @@ class ReplFromTerminal(
}
private fun one(next: WhatNextAfterOneLine): WhatNextAfterOneLine {
var line = commandReader.readLine(next) ?: return WhatNextAfterOneLine.QUIT
var line = replConfiguration.commandReader.readLine(next) ?: return WhatNextAfterOneLine.QUIT
line = unescapeLineBreaks(line)
@@ -123,25 +132,35 @@ class ReplFromTerminal(
@Throws(Exception::class)
private fun oneCommand(command: String): Boolean {
val split = splitCommand(command)
if (split.size >= 1 && command == "help") {
writer.printlnHelpMessage("Available commands:\n" +
":help show this help\n" +
":quit exit the interpreter\n" +
":dump bytecode dump classes to terminal\n" +
":load <file> load script from specified file")
if (split.isNotEmpty() && command == "help") {
val commands = mapOf("help" to "show this help",
"load <file>" to "load script from specified file",
"classes" to "dump classes to terminal",
"quit" to "quit the interpreter")
val maxCommandLength = commands.keys.fold(0) { max, s -> maxOf(max, s.length) } + 4
val renderedCommands = commands.entries.joinToString("\n") {
":" + it.key + " ".repeat(maxCommandLength - it.key.length) + it.value
}
writer.printlnHelpMessage("Available commands:\n" + renderedCommands)
return true
}
else if (split.size >= 2 && split[0] == "dump" && split[1] == "bytecode") {
else if (split.size == 1 && split.single() == "classes") {
replInterpreter.dumpClasses(PrintWriter(System.out))
return true
}
else if (split.size >= 1 && split[0] == "quit") {
else if (split.size == 1 && split.single() == "quit") {
return false
}
else if (split.size >= 2 && split[0] == "load") {
else if (split.size == 2 && split[0] == "load") {
val fileName = split[1]
val scriptText = FileUtil.loadFile(File(fileName))
eval(scriptText)
try {
val scriptText = FileUtil.loadFile(File(fileName))
eval(scriptText)
} catch (e: IOException) {
writer.outputCompileError("Can not load script: ${e.message}")
}
return true
}
else {
@@ -157,12 +176,13 @@ class ReplFromTerminal(
fun run(disposable: Disposable, configuration: CompilerConfiguration) {
val replIdeMode = System.getProperty("kotlin.repl.ideMode") == "true"
val replConfiguration = if (replIdeMode) ReplForIdeConfiguration() else ConsoleReplConfiguration()
val replConfiguration = if (replIdeMode) IdeReplConfiguration() else ConsoleReplConfiguration()
return try {
ReplFromTerminal(disposable, configuration, replConfiguration).doRun()
}
catch (e: Exception) {
replConfiguration.errorLogger.logException(e)
replConfiguration.exceptionReporter.report(e)
throw e
}
}
}

View File

@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.repl.configuration.ReplConfiguration
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.KotlinScriptDefinition
@@ -35,8 +36,7 @@ class ReplInterpreter(
disposable: Disposable,
private val configuration: CompilerConfiguration,
private val replConfiguration: ReplConfiguration
): ReplConfiguration by replConfiguration {
) {
private val lineNumber = AtomicInteger()
private val previousIncompleteLines = arrayListOf<String>()
@@ -48,7 +48,6 @@ class ReplInterpreter(
else -> null
}
}
private val classLoader = ReplClassLoader(URLClassLoader(classpathRoots.map { it.toURI().toURL() }.toTypedArray(), null))
private val messageCollector = object : MessageCollector {
private var hasErrors = false
@@ -80,7 +79,7 @@ class ReplInterpreter(
GenericReplCompiler(disposable, REPL_LINE_AS_SCRIPT_DEFINITION, configuration, messageCollector)
}
private val scriptEvaluator: ReplFullEvaluator by lazy {
GenericReplCompilingEvaluator(scriptCompiler, classpathRoots, classLoader, null, ReplRepeatingMode.REPEAT_ANY_PREVIOUS)
GenericReplCompilingEvaluator(scriptCompiler, classpathRoots, null, null, ReplRepeatingMode.REPEAT_ANY_PREVIOUS)
}
private val evalState by lazy { scriptEvaluator.createState() }
@@ -92,36 +91,26 @@ class ReplInterpreter(
try {
val evalRes = scriptEvaluator.compileAndEval(evalState, ReplCodeLine(lineNumber.getAndIncrement(), 0, fullText), null, object : InvokeWrapper {
override fun <T> invoke(body: () -> T): T = executeUserCode { body() }
override fun <T> invoke(body: () -> T): T = replConfiguration.executionInterceptor.execute(body)
})
when {
evalRes !is ReplEvalResult.Incomplete -> previousIncompleteLines.clear()
allowIncompleteLines -> previousIncompleteLines.add(line)
replConfiguration.allowIncompleteLines -> previousIncompleteLines.add(line)
else -> return ReplEvalResult.Error.CompileTime("incomplete code")
}
return evalRes
}
catch (e: Throwable) {
val writer = PrintWriter(System.err)
classLoader.dumpClasses(writer)
scriptEvaluator.classLoader.dumpClasses(writer)
writer.flush()
throw e
}
}
private fun <T> executeUserCode(body: () -> T): T {
try {
onUserCodeExecuting(true)
return body()
}
finally {
onUserCodeExecuting(false)
}
}
fun dumpClasses(out: PrintWriter) {
classLoader.dumpClasses(out)
scriptEvaluator.classLoader.dumpClasses(out)
}
companion object {

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.configuration
import org.jetbrains.kotlin.cli.jvm.repl.ReplExceptionReporter
import org.jetbrains.kotlin.cli.jvm.repl.messages.ConsoleDiagnosticMessageHolder
import org.jetbrains.kotlin.cli.jvm.repl.reader.ConsoleReplCommandReader
import org.jetbrains.kotlin.cli.jvm.repl.writer.ConsoleReplWriter
class ConsoleReplConfiguration : ReplConfiguration {
override val writer = ConsoleReplWriter()
override val exceptionReporter
get() = ReplExceptionReporter
override val commandReader = ConsoleReplCommandReader()
override val allowIncompleteLines: Boolean
get() = true
override val executionInterceptor
get() = SnippetExecutionInterceptor
override fun createDiagnosticHolder() = ConsoleDiagnosticMessageHolder()
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.configuration
import org.jetbrains.kotlin.cli.jvm.repl.IdeReplExceptionReporter
import org.jetbrains.kotlin.cli.jvm.repl.ReplExceptionReporter
import org.jetbrains.kotlin.cli.jvm.repl.messages.IdeDiagnosticMessageHolder
import org.jetbrains.kotlin.cli.jvm.repl.reader.IdeReplCommandReader
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandReader
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplSystemInWrapper
import org.jetbrains.kotlin.cli.jvm.repl.writer.IdeSystemOutWrapperReplWriter
import org.jetbrains.kotlin.cli.jvm.repl.writer.ReplWriter
class IdeReplConfiguration : ReplConfiguration {
override val allowIncompleteLines: Boolean
get() = false
override val executionInterceptor: SnippetExecutionInterceptor = object : SnippetExecutionInterceptor {
override fun <T> execute(block: () -> T): T {
try {
sinWrapper.isReplScriptExecuting = true
return block()
} finally {
sinWrapper.isReplScriptExecuting = false
}
}
}
override fun createDiagnosticHolder() = IdeDiagnosticMessageHolder()
override val writer: ReplWriter
override val exceptionReporter: ReplExceptionReporter
override val commandReader: ReplCommandReader
val sinWrapper: ReplSystemInWrapper
init {
// wrapper for `out` is required to escape every input in [ideMode];
// if [ideMode == false] then just redirects all input to [System.out]
// if user calls [System.setOut(...)] then undefined behaviour
val soutWrapper = IdeSystemOutWrapperReplWriter(System.out)
System.setOut(soutWrapper)
// wrapper for `in` is required to give user possibility of calling
// [readLine] from ide-console repl
sinWrapper = ReplSystemInWrapper(System.`in`, soutWrapper)
System.setIn(sinWrapper)
writer = soutWrapper
exceptionReporter = IdeReplExceptionReporter(writer)
commandReader = IdeReplCommandReader()
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.configuration
import org.jetbrains.kotlin.cli.jvm.repl.ReplExceptionReporter
import org.jetbrains.kotlin.cli.jvm.repl.messages.*
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandReader
import org.jetbrains.kotlin.cli.jvm.repl.writer.ReplWriter
interface ReplConfiguration {
val writer: ReplWriter
val exceptionReporter: ReplExceptionReporter
val commandReader: ReplCommandReader
val allowIncompleteLines: Boolean
val executionInterceptor: SnippetExecutionInterceptor
fun createDiagnosticHolder(): DiagnosticMessageHolder
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.configuration
interface SnippetExecutionInterceptor {
fun <T> execute(block: () -> T): T
companion object Plain : SnippetExecutionInterceptor {
override fun <T> execute(block: () -> T) = block()
}
}

View File

@@ -22,20 +22,16 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.nio.ByteBuffer
class ReplTerminalDiagnosticMessageHolder : MessageCollectorBasedReporter, DiagnosticMessageHolder {
class ConsoleDiagnosticMessageHolder : MessageCollectorBasedReporter, DiagnosticMessageHolder {
private val outputStream = ByteArrayOutputStream()
override val messageCollector: GroupingMessageCollector = GroupingMessageCollector(
PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.WITHOUT_PATHS, false),
false
)
false)
override val renderedDiagnostics: String
get() {
messageCollector.flush()
val bytes = outputStream.toByteArray()
return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString()
}
override fun renderMessage(): String {
messageCollector.flush()
return outputStream.toString("UTF-8")
}
}

View File

@@ -19,5 +19,5 @@ package org.jetbrains.kotlin.cli.jvm.repl.messages
import org.jetbrains.kotlin.cli.common.messages.DiagnosticMessageReporter
interface DiagnosticMessageHolder : DiagnosticMessageReporter {
val renderedDiagnostics: String
fun renderMessage(): String
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.messages
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.w3c.dom.ls.DOMImplementationLS
import javax.xml.parsers.DocumentBuilderFactory
class IdeDiagnosticMessageHolder : DiagnosticMessageHolder {
private val diagnostics = arrayListOf<Pair<Diagnostic, String>>()
override fun report(diagnostic: Diagnostic, file: PsiFile, render: String) {
diagnostics.add(Pair(diagnostic, render))
}
override fun renderMessage(): String {
val docFactory = DocumentBuilderFactory.newInstance()
val docBuilder = docFactory.newDocumentBuilder()
val errorReport = docBuilder.newDocument()
val rootElement = errorReport.createElement("report")
errorReport.appendChild(rootElement)
for ((diagnostic, message) in diagnostics) {
val errorRange = DiagnosticUtils.firstRange(diagnostic.textRanges)
val reportEntry = errorReport.createElement("reportEntry")
reportEntry.setAttribute("severity", diagnostic.severity.toString())
reportEntry.setAttribute("rangeStart", errorRange.startOffset.toString())
reportEntry.setAttribute("rangeEnd", errorRange.endOffset.toString())
reportEntry.appendChild(errorReport.createTextNode(StringUtil.escapeXml(message)))
rootElement.appendChild(reportEntry)
}
val domImplementation = errorReport.implementation as DOMImplementationLS
val lsSerializer = domImplementation.createLSSerializer()
return lsSerializer.writeToString(errorReport)
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.messages
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.w3c.dom.ls.DOMImplementationLS
import javax.xml.parsers.DocumentBuilderFactory
class ReplIdeDiagnosticMessageHolder : DiagnosticMessageHolder {
private val diagnostics = arrayListOf<Pair<Diagnostic, String>>()
override fun report(diagnostic: Diagnostic, file: PsiFile, render: String) {
diagnostics.add(Pair(diagnostic, render))
}
override val renderedDiagnostics: String
get() {
val docFactory = DocumentBuilderFactory.newInstance()
val docBuilder = docFactory.newDocumentBuilder()
val errorReport = docBuilder.newDocument()
val rootElement = errorReport.createElement("report")
errorReport.appendChild(rootElement)
for ((diagnostic, message) in diagnostics) {
val errorRange = DiagnosticUtils.firstRange(diagnostic.textRanges)
val reportEntry = errorReport.createElement("reportEntry")
reportEntry.setAttribute("severity", diagnostic.severity.toString())
reportEntry.setAttribute("rangeStart", errorRange.startOffset.toString())
reportEntry.setAttribute("rangeEnd", errorRange.endOffset.toString())
reportEntry.appendChild(errorReport.createTextNode(StringUtil.escapeXml(message)))
rootElement.appendChild(reportEntry)
}
val domImplementation = errorReport.implementation as DOMImplementationLS
val lsSerializer = domImplementation.createLSSerializer()
return lsSerializer.writeToString(errorReport)
}
}

View File

@@ -17,24 +17,9 @@
package org.jetbrains.kotlin.cli.jvm.repl.messages
import com.intellij.openapi.util.text.StringUtil
import org.w3c.dom.Element
import org.xml.sax.InputSource
import java.io.ByteArrayInputStream
import javax.xml.parsers.DocumentBuilderFactory
// using '#' to avoid collisions with xml escaping
val SOURCE_CHARS: Array<String> = arrayOf("\n", "#")
val XML_REPLACEMENTS: Array<String> = arrayOf("#n", "#diez")
fun parseXml(inputMessage: String): String {
fun strToSource(s: String) = InputSource(ByteArrayInputStream(s.toByteArray()))
val docFactory = DocumentBuilderFactory.newInstance()
val docBuilder = docFactory.newDocumentBuilder()
val input = docBuilder.parse(strToSource(inputMessage))
val root = input.firstChild as Element
return root.textContent
}
internal val SOURCE_CHARS: Array<String> = arrayOf("\n", "#")
internal val XML_REPLACEMENTS: Array<String> = arrayOf("#n", "#diez")
fun unescapeLineBreaks(s: String) = StringUtil.replace(s, XML_REPLACEMENTS, SOURCE_CHARS)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,17 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.messages
package org.jetbrains.kotlin.cli.jvm.repl.reader
import org.jetbrains.kotlin.cli.jvm.repl.messages.unescapeLineBreaks
import org.jetbrains.kotlin.cli.jvm.repl.writer.END_LINE
import org.jetbrains.kotlin.cli.jvm.repl.writer.ReplWriter
import org.w3c.dom.Element
import org.xml.sax.InputSource
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import javax.xml.parsers.DocumentBuilderFactory
class ReplSystemInWrapper(
private val stdin: InputStream,
@@ -87,4 +94,15 @@ class ReplSystemInWrapper(
isLastByteProcessed = true
}
}
}
private fun parseXml(inputMessage: String): String {
fun strToSource(s: String) = InputSource(ByteArrayInputStream(s.toByteArray()))
val docFactory = DocumentBuilderFactory.newInstance()
val docBuilder = docFactory.newDocumentBuilder()
val input = docBuilder.parse(strToSource(inputMessage))
val root = input.firstChild as Element
return root.textContent
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.messages
package org.jetbrains.kotlin.cli.jvm.repl.writer
class ReplConsoleWriter : ReplWriter {
class ConsoleReplWriter : ReplWriter {
override fun printlnWelcomeMessage(x: String) = println(x)
override fun printlnHelpMessage(x: String) = println(x)
override fun outputCompileError(x: String) = println(x)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,30 +14,20 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.messages
package org.jetbrains.kotlin.cli.jvm.repl.writer
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.LineSeparator
import org.jetbrains.kotlin.cli.jvm.repl.messages.SOURCE_CHARS
import org.jetbrains.kotlin.cli.jvm.repl.messages.XML_REPLACEMENTS
import org.jetbrains.kotlin.utils.repl.ReplEscapeType
import java.io.PrintStream
import org.jetbrains.kotlin.utils.repl.ReplEscapeType.*
val END_LINE: String = LineSeparator.getSystemLineSeparator().separatorString
val XML_PREAMBLE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
class ReplSystemOutWrapperForIde(standardOut: PrintStream) : PrintStream(standardOut, true), ReplWriter {
private enum class EscapeType {
INITIAL_PROMPT,
HELP_PROMPT,
USER_OUTPUT,
REPL_RESULT,
READLINE_START,
READLINE_END,
REPL_INCOMPLETE,
COMPILE_ERROR,
RUNTIME_ERROR,
INTERNAL_ERROR,
SUCCESS
}
internal val END_LINE: String = LineSeparator.getSystemLineSeparator().separatorString
internal val XML_PREAMBLE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
class IdeSystemOutWrapperReplWriter(standardOut: PrintStream) : PrintStream(standardOut, true), ReplWriter {
override fun print(x: Boolean) = printWithEscaping(x.toString())
override fun print(x: Char) = printWithEscaping(x.toString())
override fun print(x: Int) = printWithEscaping(x.toString())
@@ -47,25 +37,27 @@ class ReplSystemOutWrapperForIde(standardOut: PrintStream) : PrintStream(standar
override fun print(x: String) = printWithEscaping(x)
override fun print(x: Any?) = printWithEscaping(x.toString())
private fun printlnWithEscaping(text: String, escapeType: EscapeType = EscapeType.USER_OUTPUT) = printWithEscaping("$text\n", escapeType)
private fun printWithEscaping(text: String, escapeType: EscapeType = EscapeType.USER_OUTPUT) {
super.print("${xmlEscape(text, escapeType)}$END_LINE")
private fun printlnWithEscaping(text: String, escapeType: ReplEscapeType = USER_OUTPUT) {
printWithEscaping("$text\n", escapeType)
}
private fun xmlEscape(s: String, escapeType: EscapeType): String {
private fun printWithEscaping(text: String, escapeType: ReplEscapeType = USER_OUTPUT) {
super.print("${xmlEscape(text, escapeType)}${END_LINE}")
}
private fun xmlEscape(s: String, escapeType: ReplEscapeType): String {
val singleLine = StringUtil.replace(s, SOURCE_CHARS, XML_REPLACEMENTS)
return "$XML_PREAMBLE<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
return "${XML_PREAMBLE}<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
}
override fun printlnWelcomeMessage(x: String) = printlnWithEscaping(x, EscapeType.INITIAL_PROMPT)
override fun printlnHelpMessage(x: String) = printlnWithEscaping(x, EscapeType.HELP_PROMPT)
override fun outputCommandResult(x: String) = printlnWithEscaping(x, EscapeType.REPL_RESULT)
override fun notifyReadLineStart() = printlnWithEscaping("", EscapeType.READLINE_START)
override fun notifyReadLineEnd() = printlnWithEscaping("", EscapeType.READLINE_END)
override fun notifyCommandSuccess() = printlnWithEscaping("", EscapeType.SUCCESS)
override fun notifyIncomplete() = printlnWithEscaping("", EscapeType.REPL_INCOMPLETE)
override fun outputCompileError(x: String) = printlnWithEscaping(x, EscapeType.COMPILE_ERROR)
override fun outputRuntimeError(x: String) = printlnWithEscaping(x, EscapeType.RUNTIME_ERROR)
override fun sendInternalErrorReport(x: String) = printlnWithEscaping(x, EscapeType.INTERNAL_ERROR)
override fun printlnWelcomeMessage(x: String) = printlnWithEscaping(x, INITIAL_PROMPT)
override fun printlnHelpMessage(x: String) = printlnWithEscaping(x, HELP_PROMPT)
override fun outputCommandResult(x: String) = printlnWithEscaping(x, REPL_RESULT)
override fun notifyReadLineStart() = printlnWithEscaping("", READLINE_START)
override fun notifyReadLineEnd() = printlnWithEscaping("", READLINE_END)
override fun notifyCommandSuccess() = printlnWithEscaping("", SUCCESS)
override fun notifyIncomplete() = printlnWithEscaping("", REPL_INCOMPLETE)
override fun outputCompileError(x: String) = printlnWithEscaping(x, COMPILE_ERROR)
override fun outputRuntimeError(x: String) = printlnWithEscaping(x, RUNTIME_ERROR)
override fun sendInternalErrorReport(x: String) = printlnWithEscaping(x, INTERNAL_ERROR)
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.messages
package org.jetbrains.kotlin.cli.jvm.repl.writer
interface ReplWriter {
fun printlnWelcomeMessage(x: String)

View File

@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.resolve.annotations
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -29,6 +31,13 @@ val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic")
val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField")
fun DeclarationDescriptor.hasJvmStaticAnnotation(): Boolean {
if (this is CallableDescriptor) {
val containingDeclaration = this.containingDeclaration
if (containingDeclaration is ScriptDescriptor && containingDeclaration.isReplSnippet) {
return true
}
}
return annotations.findAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) != null
}

View File

@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.utils.repl.IS_REPL_SNIPPET
class KtScriptInfo(
val script: KtScript
@@ -32,7 +33,10 @@ class KtScriptInfo(
override fun getCorrespondingClassOrObject() = null
override fun getTypeParameterList() = null
override fun getPrimaryConstructorParameters() = listOf<KtParameter>()
override fun getClassKind() = ClassKind.CLASS
override fun getClassKind() = if (isReplSnippet) ClassKind.OBJECT else ClassKind.CLASS
override fun getDeclarations() = script.declarations
override fun getDanglingAnnotations() = listOf<KtAnnotationEntry>()
val isReplSnippet: Boolean
get() = script.getUserData(IS_REPL_SNIPPET) == true
}

View File

@@ -100,7 +100,13 @@ class ClassResolutionScopesSupport(
if (withCompanionObject) {
staticScopes.addIfNotNull(classDescriptor.companionObjectDescriptor?.unsubstitutedInnerClassesScope)
implicitReceiver = classDescriptor.companionObjectDescriptor?.thisAsReceiverParameter
if (classDescriptor is LazyScriptDescriptor && classDescriptor.isReplSnippet) {
implicitReceiver = null
}
else {
implicitReceiver = classDescriptor.companionObjectDescriptor?.thisAsReceiverParameter
}
parentForNewScope = classDescriptor.companionObjectDescriptor?.let {
it.getAllSuperclassesWithoutAny().asReversed().fold(parent) { scope, currentClass ->

View File

@@ -50,6 +50,8 @@ class LazyScriptDescriptor(
resolveSession.trace.record(BindingContext.SCRIPT, scriptInfo.script, this)
}
override fun isReplSnippet() = scriptInfo.isReplSnippet
private val sourceElement = scriptInfo.script.toSourceElement()
override fun getSource() = sourceElement

View File

@@ -20,16 +20,26 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.utils.Printer
class LexicalScopeImpl @JvmOverloads constructor(
parent: HierarchicalScope,
override val ownerDescriptor: DeclarationDescriptor,
override val isOwnerDescriptorAccessibleByLabel: Boolean,
override val implicitReceiver: ReceiverParameterDescriptor?,
override val kind: LexicalScopeKind,
redeclarationChecker: LocalRedeclarationChecker = LocalRedeclarationChecker.DO_NOTHING,
initialize: LexicalScopeImpl.InitializeHandler.() -> Unit = {}
) : LexicalScope, LexicalScopeStorage(parent, redeclarationChecker) {
parent: HierarchicalScope,
override val ownerDescriptor: DeclarationDescriptor,
override val isOwnerDescriptorAccessibleByLabel: Boolean,
implicitReceiver: ReceiverParameterDescriptor?,
override val kind: LexicalScopeKind,
redeclarationChecker: LocalRedeclarationChecker = LocalRedeclarationChecker.DO_NOTHING,
initialize: LexicalScopeImpl.InitializeHandler.() -> Unit = {}
): LexicalScope, LexicalScopeStorage(parent, redeclarationChecker) {
override val implicitReceiver: ReceiverParameterDescriptor?
init {
if (ownerDescriptor is ScriptDescriptor && ownerDescriptor.isReplSnippet) {
// Do not allow 'this' in scripts
this.implicitReceiver = null
}
else {
this.implicitReceiver = implicitReceiver
}
InitializeHandler().initialize()
}

View File

@@ -7,6 +7,6 @@ error: unresolved reference: a
a
^
>>> val s: String = s
error: variable 's' must be initialized
error: unresolved reference: s
val s: String = s
^

View File

@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.repl
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.cli.jvm.repl.ConsoleReplConfiguration
import org.jetbrains.kotlin.cli.jvm.repl.configuration.ConsoleReplConfiguration
import org.jetbrains.kotlin.cli.jvm.repl.ReplInterpreter
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.utils.repl
import com.intellij.openapi.util.Key
val IS_REPL_SNIPPET = Key<Boolean>("IS_REPL_SNIPPET")
enum class ReplEscapeType {
INITIAL_PROMPT,
HELP_PROMPT,
USER_OUTPUT,
REPL_RESULT,
READLINE_START,
READLINE_END,
REPL_INCOMPLETE,
COMPILE_ERROR,
RUNTIME_ERROR,
INTERNAL_ERROR,
SUCCESS;
companion object {
fun valueOfOrNull(string: String): ReplEscapeType? {
return try {
valueOf(string)
}
catch (e: IllegalArgumentException) {
null
}
}
}
}

View File

@@ -21,6 +21,8 @@ import org.jetbrains.annotations.NotNull;
public interface ScriptDescriptor extends ClassDescriptor {
int getPriority();
boolean isReplSnippet();
@NotNull
@Override
ClassConstructorDescriptor getUnsubstitutedPrimaryConstructor();

View File

@@ -24,11 +24,13 @@ import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.annotations.NotNull
import org.jetbrains.kotlin.console.actions.logError
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.utils.repl.ReplEscapeType
import org.w3c.dom.Element
import org.xml.sax.InputSource
import java.io.ByteArrayInputStream
import java.nio.charset.Charset
import javax.xml.parsers.DocumentBuilderFactory
import org.jetbrains.kotlin.utils.repl.ReplEscapeType.*
val XML_REPLACEMENTS: Array<String> = arrayOf("#n", "#diez")
val SOURCE_CHARS: Array<String> = arrayOf("\n", "#")
@@ -76,25 +78,25 @@ class ReplOutputHandler(
}
val root = output.firstChild as Element
val outputType = root.getAttribute("type")
val outputType = ReplEscapeType.valueOfOrNull(root.getAttribute("type"))
val content = StringUtil.replace(root.textContent, XML_REPLACEMENTS, SOURCE_CHARS)
when (outputType) {
"INITIAL_PROMPT" -> buildWarningIfNeededBeforeInit(content)
"HELP_PROMPT" -> outputProcessor.printHelp(content)
"USER_OUTPUT" -> outputProcessor.printUserOutput(content)
"REPL_RESULT" -> outputProcessor.printResultWithGutterIcon(content)
"READLINE_START" -> runner.isReadLineMode = true
"READLINE_END" -> runner.isReadLineMode = false
"REPL_INCOMPLETE",
"COMPILE_ERROR" -> outputProcessor.highlightCompilerErrors(createCompilerMessages(content))
"RUNTIME_ERROR" -> outputProcessor.printRuntimeError("${content.trim()}\n")
"INTERNAL_ERROR" -> outputProcessor.printInternalErrorMessage(content)
"SUCCESS" -> runner.commandHistory.lastUnprocessedEntry()?.entryText?.let { runner.successfulLine(it) }
else -> logError(ReplOutputHandler::class.java, "Unexpected output type:\n$outputType")
INITIAL_PROMPT -> buildWarningIfNeededBeforeInit(content)
HELP_PROMPT -> outputProcessor.printHelp(content)
USER_OUTPUT -> outputProcessor.printUserOutput(content)
REPL_RESULT -> outputProcessor.printResultWithGutterIcon(content)
READLINE_START -> runner.isReadLineMode = true
READLINE_END -> runner.isReadLineMode = false
REPL_INCOMPLETE,
COMPILE_ERROR -> outputProcessor.highlightCompilerErrors(createCompilerMessages(content))
RUNTIME_ERROR -> outputProcessor.printRuntimeError("${content.trim()}\n")
INTERNAL_ERROR -> outputProcessor.printInternalErrorMessage(content)
SUCCESS -> runner.commandHistory.lastUnprocessedEntry()?.entryText?.let { runner.successfulLine(it) }
null -> logError(ReplOutputHandler::class.java, "Unexpected output type:\n$outputType")
}
if (outputType in setOf("SUCCESS", "COMPILE_ERROR", "INTERNAL_ERROR", "RUNTIME_ERROR", "READLINE_END")) {
if (outputType in setOf(SUCCESS, COMPILE_ERROR, INTERNAL_ERROR, RUNTIME_ERROR, READLINE_END)) {
runner.commandHistory.entryProcessed()
}
}