mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-11 00:21:31 +00:00
Compare commits
28 Commits
rr/stdlib/
...
yan.archiv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a2457ca58 | ||
|
|
aec1f79217 | ||
|
|
63a02f9247 | ||
|
|
f6e9c2507d | ||
|
|
55a82615c8 | ||
|
|
64230721ae | ||
|
|
72fd50cd69 | ||
|
|
eefe6cdfbc | ||
|
|
a92b58d7ea | ||
|
|
1c7169e6b8 | ||
|
|
8017da80da | ||
|
|
2914b11097 | ||
|
|
95ea14feb1 | ||
|
|
079360a4fe | ||
|
|
047b222240 | ||
|
|
e772054a37 | ||
|
|
8c6ea5135b | ||
|
|
6075927624 | ||
|
|
ba526c91c9 | ||
|
|
1e2de8b113 | ||
|
|
648f9aae52 | ||
|
|
a1d68f4a79 | ||
|
|
08590c676c | ||
|
|
668570e6b0 | ||
|
|
0205c7d153 | ||
|
|
8351567f46 | ||
|
|
c5137be433 | ||
|
|
0238575a36 |
1
.idea/dictionaries/yan.xml
generated
1
.idea/dictionaries/yan.xml
generated
@@ -5,6 +5,7 @@
|
||||
<w>impls</w>
|
||||
<w>kapt</w>
|
||||
<w>parceler</w>
|
||||
<w>repl</w>
|
||||
<w>uast</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -23,6 +23,11 @@ dependencies {
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
|
||||
|
||||
runtime(projectDist(":kotlin-reflect"))
|
||||
|
||||
// Used in REPL code insight
|
||||
compileOnly(project(":idea:idea-core"))
|
||||
|
||||
testCompile(project(":compiler:backend"))
|
||||
testCompile(project(":compiler:cli"))
|
||||
testCompile(project(":compiler:tests-common"))
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -23,20 +23,21 @@ 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,
|
||||
scriptArgs: ScriptArgsWithTypes?,
|
||||
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
state.lock.write {
|
||||
val evalState = state.asState(GenericReplEvaluatorState::class.java)
|
||||
val evalState = state.asState<GenericReplEvaluatorState>()
|
||||
val historyActor = when (repeatingMode) {
|
||||
ReplRepeatingMode.NONE -> HistoryActionsForNoRepeat(evalState)
|
||||
ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT -> {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
val state: IReplStageState<*> // The Invokable interface do not allow Context/Bindings substitution, so state is supplied via property
|
||||
|
||||
private fun prioritizedHistory(receiverClass: KClass<*>?, receiverInstance: Any?): List<EvalClassWithInstanceAndLoader> {
|
||||
val evalState = state.asState(GenericReplEvaluatorState::class.java)
|
||||
val evalState = state.asState<GenericReplEvaluatorState>()
|
||||
return evalState.history.map { it.item }.filter { it.instance != null }.reversed().ensureNotEmpty("no script ").let { history ->
|
||||
if (receiverInstance != null) {
|
||||
val receiverKlass = receiverClass ?: receiverInstance::class
|
||||
|
||||
@@ -157,7 +157,9 @@ sealed class ReplEvalResult : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction
|
||||
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction {
|
||||
val classLoader: ReplClassLoader
|
||||
}
|
||||
|
||||
// --- compileAdnEval
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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>()
|
||||
private val loadedClasses = HashMap<JvmClassName, Class<*>>()
|
||||
|
||||
fun addJar(file: File) {
|
||||
super.addURL(file.toURI().toURL())
|
||||
}
|
||||
|
||||
override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
||||
val className = JvmClassName.byFqNameWithoutInnerClasses(name)
|
||||
|
||||
val classBytes = classes[className]
|
||||
if (classBytes != null) {
|
||||
return loadedClasses.getOrPut(className) {
|
||||
defineClass(name, classBytes, 0, classBytes.size)
|
||||
}
|
||||
}
|
||||
|
||||
return super.loadClass(name, resolve)
|
||||
}
|
||||
|
||||
override fun findClass(name: String): Class<*> {
|
||||
val className = JvmClassName.byFqNameWithoutInnerClasses(name)
|
||||
|
||||
val classBytes = classes[className]
|
||||
if (classBytes != null) {
|
||||
return loadedClasses.getOrPut(className) {
|
||||
defineClass(name, classBytes, 0, classBytes.size)
|
||||
}
|
||||
}
|
||||
|
||||
return 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,52 @@ 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified StateT : IReplStageState<*>> IReplStageState<*>.asState(): StateT {
|
||||
return asState(StateT::class.java)
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.KotlinRepl
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
@@ -103,7 +103,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
messageCollector.report(ERROR, "Specify script source path to evaluate")
|
||||
return COMPILATION_ERROR
|
||||
}
|
||||
ReplFromTerminal.run(rootDisposable, configuration)
|
||||
KotlinRepl.run(rootDisposable, configuration)
|
||||
return ExitCode.OK
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,11 +65,11 @@ 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 {
|
||||
val checkerState = state.asState(GenericReplCheckerState::class.java)
|
||||
val checkerState = state.asState<GenericReplCheckerState>()
|
||||
val scriptFileName = makeScriptBaseName(codeLine)
|
||||
val virtualFile =
|
||||
LightVirtualFile("$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(codeLine.code)).apply {
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
@@ -35,15 +36,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 {
|
||||
|
||||
@@ -56,9 +62,16 @@ open class GenericReplCompiler(disposable: Disposable,
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = checker.check(state, codeLine)
|
||||
|
||||
fun extractSnippetExpression(file: PsiFile): KtExpression? {
|
||||
return file.getChildOfType<KtScript>()
|
||||
?.getChildOfType<KtBlockExpression>()
|
||||
?.getChildOfType<KtScriptInitializer>()
|
||||
?.getChildOfType<KtExpression>()
|
||||
}
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
|
||||
state.lock.write {
|
||||
val compilerState = state.asState(GenericReplCompilerState::class.java)
|
||||
val compilerState = state.asState<GenericReplCompilerState>()
|
||||
|
||||
val (psiFile, errorHolder) = run {
|
||||
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
||||
@@ -79,10 +92,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,10 +124,7 @@ open class GenericReplCompiler(disposable: Disposable,
|
||||
val generatedClassname = makeScriptBaseName(codeLine)
|
||||
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
||||
|
||||
val expression = psiFile.getChildOfType<KtScript>()?.
|
||||
getChildOfType<KtBlockExpression>()?.
|
||||
getChildOfType<KtScriptInitializer>()?.
|
||||
getChildOfType<KtExpression>()
|
||||
val expression = extractSnippetExpression(psiFile)
|
||||
|
||||
val type = expression?.let {
|
||||
compilerState.analyzerEngine.trace.bindingContext.getType(it)
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 com.google.common.hash.Hashing
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import java.io.File
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.util.jar.JarFile
|
||||
|
||||
class KotlinLibraryNameCache(val project: Project) {
|
||||
private companion object {
|
||||
val cacheDirectory = File(
|
||||
System.getProperty("user.home")
|
||||
?: error("Can't find home directory"), ".kotlin/repl/libraryCache"
|
||||
).apply { mkdirs() }
|
||||
}
|
||||
|
||||
val ktFactory = KtPsiFactory(project)
|
||||
|
||||
private val librariesWithSources = mutableMapOf<File, Boolean>()
|
||||
private val libraryCache = mutableMapOf<File, LibraryNames>()
|
||||
|
||||
fun getEntryName(library: File, packageName: String, declarationName: String): String? {
|
||||
processAndCacheIfNeeded(library)
|
||||
|
||||
val libraryNames = libraryCache[library] ?: return null
|
||||
return (libraryNames[packageName] ?: return null)[declarationName]
|
||||
}
|
||||
|
||||
fun hasSources(library: File): Boolean {
|
||||
processAndCacheIfNeeded(library)
|
||||
|
||||
return librariesWithSources[library] ?: false
|
||||
}
|
||||
|
||||
private fun processAndCacheIfNeeded(library: File) {
|
||||
val cacheFile = File(cacheDirectory, getFileIdentifier(library))
|
||||
if (!cacheFile.exists()) {
|
||||
processAndCache(library)
|
||||
} else if (library !in libraryCache) {
|
||||
loadLibraryFromCache(library, cacheFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadLibraryFromCache(library: File, cacheFile: File) {
|
||||
val (sourceFilesFound, libraryNames) = cacheFile.inputStream().use {
|
||||
ObjectInputStream(it).run {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
Pair(readBoolean(), readObject() as? LibraryNames)
|
||||
}
|
||||
}
|
||||
|
||||
libraryCache[library] = libraryNames ?: emptyMap()
|
||||
librariesWithSources[library] = sourceFilesFound
|
||||
}
|
||||
|
||||
private fun processAndCache(library: File): LibraryNames {
|
||||
if (!library.isFile) {
|
||||
return emptyMap()
|
||||
}
|
||||
|
||||
try {
|
||||
val namesForFile: MutableLibraryNames = mutableMapOf()
|
||||
val sourceFilesFound = processLibrary(library, namesForFile)
|
||||
|
||||
libraryCache[library] = namesForFile
|
||||
librariesWithSources[library] = sourceFilesFound
|
||||
|
||||
File(cacheDirectory, getFileIdentifier(library)).outputStream().use { os ->
|
||||
ObjectOutputStream(os).use { oos ->
|
||||
oos.writeBoolean(sourceFilesFound)
|
||||
oos.writeObject(namesForFile)
|
||||
}
|
||||
}
|
||||
|
||||
return namesForFile
|
||||
} catch (e: Throwable) {
|
||||
throw RuntimeException("Can not process $library", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileIdentifier(file: File): String {
|
||||
return sha1(file.absolutePath + file.length() + file.lastModified())
|
||||
}
|
||||
|
||||
private fun processLibrary(file: File, consumer: MutableLibraryNames): Boolean {
|
||||
var sourceFilesFound = false
|
||||
|
||||
JarFile(file).use { jar ->
|
||||
for (entry in jar.entries()) {
|
||||
val extension = entry.name.substringAfterLast('.', missingDelimiterValue = "")
|
||||
if (!sourceFilesFound && (extension == "kt") || extension == "java") {
|
||||
sourceFilesFound = true
|
||||
}
|
||||
|
||||
fun readEntryContents() = jar.getInputStream(entry).reader().readText()
|
||||
|
||||
if (extension == "kt") {
|
||||
val (packageName, declarationNames) = scanKotlinSource(readEntryContents())
|
||||
if (declarationNames.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
||||
val packageData = consumer.getOrPut(packageName, { mutableMapOf() })
|
||||
declarationNames.forEach { packageData[it] = entry.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sourceFilesFound
|
||||
}
|
||||
|
||||
private fun scanKotlinSource(source: String): FileNames {
|
||||
val ktFile = ktFactory.createFile(source)
|
||||
|
||||
val declarationNames = hashSetOf<String>()
|
||||
|
||||
fun processDeclaration(prefix: String, declaration: KtDeclaration) {
|
||||
if (declaration.docComment == null) {
|
||||
return
|
||||
}
|
||||
|
||||
declarationNames += prefix + declaration.name
|
||||
|
||||
if (declaration is KtClassOrObject) {
|
||||
val newPrefix = prefix + declaration.name + "."
|
||||
declaration.declarations.forEach { processDeclaration(newPrefix, it) }
|
||||
}
|
||||
}
|
||||
|
||||
ktFile.declarations.forEach { processDeclaration("", it) }
|
||||
return FileNames(ktFile.packageFqName.asString(), declarationNames)
|
||||
}
|
||||
}
|
||||
|
||||
private typealias LibraryNames = Map<String, Map<String, String>>
|
||||
private typealias MutableLibraryNames = MutableMap<String, MutableMap<String, String>>
|
||||
|
||||
private data class FileNames(val packageName: String, val declarationNames: Set<String>)
|
||||
|
||||
private fun sha1(text: String): String {
|
||||
return Hashing.sha1().hashBytes(text.toByteArray()).toString()
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
|
||||
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.reader.ReplProcessor
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class KotlinRepl(
|
||||
disposable: Disposable,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
private val replConfiguration: ReplConfiguration
|
||||
) {
|
||||
private val replInitializer: Future<ReplInterpreter> = Executors.newSingleThreadExecutor().submit(Callable {
|
||||
ReplInterpreter(disposable, compilerConfiguration, replConfiguration)
|
||||
})
|
||||
|
||||
private val replInterpreter: ReplInterpreter
|
||||
get() = replInitializer.get()
|
||||
|
||||
val writer get() = replConfiguration.writer
|
||||
|
||||
private val messageCollector = compilerConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
private fun doRun() {
|
||||
try {
|
||||
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()
|
||||
(messageCollector as? GroupingMessageCollector)?.flush()
|
||||
if (hasErrors) return
|
||||
|
||||
val processor = ReplProcessor(replInterpreter, replConfiguration)
|
||||
replConfiguration.reader.runLoop(processor)
|
||||
} catch (e: Exception) {
|
||||
replConfiguration.exceptionReporter.report(e)
|
||||
throw e
|
||||
} finally {
|
||||
try {
|
||||
replConfiguration.reader.flushHistory()
|
||||
} catch (e: Exception) {
|
||||
replConfiguration.exceptionReporter.report(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class WhatNextAfterOneLine {
|
||||
READ_LINE,
|
||||
INCOMPLETE,
|
||||
QUIT
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun run(disposable: Disposable, configuration: CompilerConfiguration) {
|
||||
val replIdeMode = System.getProperty("kotlin.repl.ideMode") == "true"
|
||||
val replConfiguration = if (replIdeMode) IdeReplConfiguration() else ConsoleReplConfiguration()
|
||||
|
||||
return try {
|
||||
KotlinRepl(disposable, configuration, replConfiguration).doRun()
|
||||
} catch (e: Exception) {
|
||||
replConfiguration.exceptionReporter.report(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.cli.jvm.repl
|
||||
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||
import java.io.File
|
||||
import java.util.jar.JarFile
|
||||
|
||||
class LibraryDocInfoProvider(val project: Project) {
|
||||
private companion object {
|
||||
private val LINE_SEPARATOR = System.getProperty("line.separator")
|
||||
|
||||
private val FILTERED_JAVA_TAGS = listOf("see", "author", "since")
|
||||
}
|
||||
|
||||
private val kotlinLibraryCache = KotlinLibraryNameCache(project)
|
||||
private val javaFileFactory = PsiFileFactory.getInstance(project)
|
||||
|
||||
fun getDoc(language: Language, libraries: List<File>, packageName: String, declarationName: String): String? {
|
||||
assert(language == KotlinLanguage.INSTANCE || language == JavaLanguage.INSTANCE)
|
||||
|
||||
for (library in libraries) {
|
||||
// Checks also for .java source files
|
||||
if (!kotlinLibraryCache.hasSources(library)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (language == KotlinLanguage.INSTANCE) {
|
||||
getKotlinDoc(library, packageName, declarationName)?.let { return it }
|
||||
}
|
||||
|
||||
if (language == JavaLanguage.INSTANCE) {
|
||||
getJavaDoc(library, packageName, declarationName)?.let { return it }
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getKotlinDoc(library: File, packageName: String, declarationName: String): String? {
|
||||
assert(declarationName.isNotEmpty())
|
||||
|
||||
val entryName = kotlinLibraryCache.getEntryName(library, packageName, declarationName) ?: return null
|
||||
JarFile(library).use { jar ->
|
||||
assert(entryName.toLowerCase().endsWith(".kt"))
|
||||
|
||||
val entry = jar.getJarEntry(entryName) ?: return null
|
||||
val ktFile = kotlinLibraryCache.ktFactory.createFile(jar.getInputStream(entry).reader().readText())
|
||||
|
||||
fun findDeclaration(current: KtElement, path: List<String>): KtDeclaration? {
|
||||
if (path.isEmpty() && current is KtNamedDeclaration) {
|
||||
return current
|
||||
}
|
||||
|
||||
if (current is KtDeclarationContainer) {
|
||||
val targetName = path.first()
|
||||
for (declaration in current.declarations) {
|
||||
if (declaration is KtNamedDeclaration && declaration.name == targetName) {
|
||||
return findDeclaration(declaration, path.drop(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
val kdoc = findDeclaration(ktFile, declarationName.split('.'))?.docComment ?: return null
|
||||
return kdoc.getChildrenOfType<KDocSection>().firstOrNull()?.text?.let { prettifyKdoc(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getJavaDoc(library: File, packageName: String, declarationName: String): String? {
|
||||
JarFile(library).use { jar ->
|
||||
val segments = declarationName.split('.')
|
||||
|
||||
// If the file has more than one top-level class, only the public one will be found
|
||||
val entryName = packageName.replace('.', '/') + (if (packageName.isEmpty()) "" else "/") + segments.first() + ".java"
|
||||
val entry = jar.getJarEntry(entryName) ?: return null
|
||||
val fileText = jar.getInputStream(entry).reader().readText()
|
||||
|
||||
val javaFile = javaFileFactory.createFileFromText(entry.name, JavaLanguage.INSTANCE, fileText) as? PsiJavaFile ?: return null
|
||||
val topLevelClass = javaFile.classes.firstOrNull { it.name == segments.first() } ?: return null
|
||||
|
||||
fun findDeclaration(element: PsiElement, segments: List<String>): PsiJavaDocumentedElement? {
|
||||
if (segments.isEmpty() && element is PsiJavaDocumentedElement) {
|
||||
return element
|
||||
}
|
||||
|
||||
assert(segments.isNotEmpty())
|
||||
val targetName = segments.first()
|
||||
|
||||
if (element is PsiClass) {
|
||||
val innerClass = element.innerClasses.firstOrNull { it.name == targetName }
|
||||
if (innerClass != null) {
|
||||
return findDeclaration(innerClass, segments.drop(1))
|
||||
}
|
||||
|
||||
if (segments.size > 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
val field = element.fields.firstOrNull { it.name == targetName }
|
||||
if (field != null) {
|
||||
return field
|
||||
}
|
||||
|
||||
val method = element.methods.firstOrNull { it.name == targetName }
|
||||
if (method != null) {
|
||||
return method
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
val javadoc = findDeclaration(topLevelClass, segments.drop(1))?.docComment ?: return null
|
||||
return prettifyJavaDoc(javadoc.text)
|
||||
}
|
||||
}
|
||||
|
||||
private fun prettifyKdoc(rawString: String): String {
|
||||
val withoutLeadingStars = rawString.trimMargin("*")
|
||||
|
||||
return withoutLeadingStars
|
||||
.lines()
|
||||
.joinToString(LINE_SEPARATOR) { it.trim() }
|
||||
.replace(Regex("\n\n\n+"), "\n\n")
|
||||
}
|
||||
|
||||
private fun prettifyJavaDoc(rawString: String): String {
|
||||
val withoutCommentSymbols = rawString.trim().substringAfter("/**").substringBeforeLast("*/").trim()
|
||||
val withoutHtmlTags = withoutCommentSymbols.replace(Regex("<[^A-Z>]*>"), "")
|
||||
val withoutLeadingStars = withoutHtmlTags.trimMargin("*")
|
||||
|
||||
return withoutLeadingStars.lines()
|
||||
.map { it.trim() }
|
||||
.filter { line -> FILTERED_JAVA_TAGS.none { line.startsWith("@$it ") } }
|
||||
.joinToString(LINE_SEPARATOR)
|
||||
.replace(Regex(" +"), " ")
|
||||
.replace(Regex("\n\n\n+"), "\n\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import java.io.File
|
||||
|
||||
class LibrarySourcesSearcher(private val compilerConfiguration: CompilerConfiguration) {
|
||||
fun findAdditionalSourceJars(classpath: List<File>): List<File> {
|
||||
return listOfNotNull(findKotlinStdlibSources(classpath), findJdkSources())
|
||||
}
|
||||
|
||||
private fun findJdkSources(): File? {
|
||||
var jdkHome = compilerConfiguration[JVMConfigurationKeys.JDK_HOME] ?: return null
|
||||
if (jdkHome.name == "jre") {
|
||||
jdkHome = jdkHome.parentFile ?: return null
|
||||
}
|
||||
|
||||
return File(jdkHome, "src.zip").takeIf { it.exists() } ?: return null
|
||||
}
|
||||
|
||||
private fun findKotlinStdlibSources(classpath: List<File>): File? {
|
||||
fun File.getClassifier(): String {
|
||||
val nameWithoutExtension = this.nameWithoutExtension
|
||||
return when {
|
||||
nameWithoutExtension.endsWith("-sources") -> "sources"
|
||||
nameWithoutExtension.endsWith("-javadoc") -> "javadoc"
|
||||
else -> "" // kotlin-stdlib does not have other classifiers for now
|
||||
}
|
||||
}
|
||||
|
||||
var stdlibSourcesJar: File? = null
|
||||
|
||||
val classpathJars = classpath.filter { it.extension.toLowerCase() == "jar" }
|
||||
|
||||
val sourceJars = classpathJars.filter { it.getClassifier() == "sources" }
|
||||
val classesJars = classpathJars.filter { it.getClassifier() == "" }
|
||||
|
||||
classesJars.find { it.name.startsWith("kotlin-stdlib") }?.let { kotlinStdlibJar ->
|
||||
if (sourceJars.any { it.name.startsWith("kotlin-stdlib-") }) {
|
||||
// There is already sources jar in classpath
|
||||
return@let
|
||||
}
|
||||
|
||||
val probablySourcesJar = File(kotlinStdlibJar.parentFile, kotlinStdlibJar.nameWithoutExtension + "-sources.jar")
|
||||
if (probablySourcesJar.exists()) {
|
||||
stdlibSourcesJar = probablySourcesJar
|
||||
}
|
||||
else {
|
||||
// We are probably inside the .gradle dir
|
||||
|
||||
val versionDir = kotlinStdlibJar.parentFile?.parentFile
|
||||
val artifactDir = versionDir?.parentFile
|
||||
val groupDir = artifactDir?.parentFile
|
||||
|
||||
// .gradle/caches/modules-2/files-2.1/groupName/artifactName/versionName/separateDirsForArtifacts
|
||||
val dotGradleDir = groupDir?.parentFile?.parentFile?.parentFile?.parentFile
|
||||
|
||||
if (dotGradleDir != null
|
||||
&& dotGradleDir.name == ".gradle"
|
||||
&& groupDir.name == "org.jetbrains.kotlin"
|
||||
&& artifactDir.name == "kotlin-stdlib"
|
||||
) {
|
||||
versionDir.listFiles { f: File -> f.isDirectory }
|
||||
.flatMap { (it.listFiles() ?: emptyArray()).asList() }
|
||||
.firstOrNull { it.isFile && it.extension.toLowerCase() == "jar" && it.getClassifier() == "sources" }
|
||||
?.let { stdlibSourcesJar = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stdlibSourcesJar
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ILineId
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
@@ -25,37 +26,39 @@ import org.jetbrains.kotlin.cli.common.repl.ReplHistory
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.container.ComponentProvider
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisContext
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
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) {
|
||||
|
||||
private val topDownAnalysisContext: TopDownAnalysisContext
|
||||
private val topDownAnalyzer: LazyTopDownAnalyzer
|
||||
private val resolveSession: ResolveSession
|
||||
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||
private val replState = ResettableAnalyzerState()
|
||||
val qualifiedExpressionResolver: QualifiedExpressionResolver
|
||||
|
||||
val project: Project
|
||||
val module: ModuleDescriptorImpl
|
||||
|
||||
val componentProvider: ComponentProvider
|
||||
|
||||
val trace: BindingTraceContext = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
|
||||
|
||||
init {
|
||||
@@ -71,9 +74,13 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
{ _, _ -> ScriptMutableDeclarationProviderFactory() }
|
||||
)
|
||||
|
||||
this.project = environment.project
|
||||
this.componentProvider = container
|
||||
|
||||
this.module = container.get<ModuleDescriptorImpl>()
|
||||
this.scriptDeclarationFactory = container.get<ScriptMutableDeclarationProviderFactory>()
|
||||
this.resolveSession = container.get<ResolveSession>()
|
||||
this.qualifiedExpressionResolver = container.get<QualifiedExpressionResolver>()
|
||||
this.topDownAnalysisContext = TopDownAnalysisContext(
|
||||
TopDownAnalysisMode.TopLevelDeclarations, DataFlowInfoFactory.EMPTY, resolveSession.declarationScopeProvider
|
||||
)
|
||||
@@ -91,6 +98,12 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
|
||||
fun lastSuccessfulLine() = replState.lastSuccessfulLine()
|
||||
|
||||
fun setDelegateFactory(linePsi: KtFile) {
|
||||
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi)))
|
||||
}
|
||||
|
||||
fun resetToLine(lineId: ILineId): List<ReplCodeLine> = replState.resetToLine(lineId)
|
||||
|
||||
fun reset(): List<ReplCodeLine> = replState.reset()
|
||||
@@ -105,7 +118,7 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
|
||||
private fun doAnalyze(linePsi: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi)))
|
||||
setDelegateFactory(linePsi)
|
||||
replState.submitLine(linePsi, codeLine)
|
||||
|
||||
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi))
|
||||
@@ -173,6 +186,8 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
||||
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
||||
|
||||
fun lastSuccessfulLine() = successfulLines.lastValue()?.lineDescriptor
|
||||
|
||||
fun resetToLine(lineId: ILineId): List<ReplCodeLine> {
|
||||
val removed = successfulLines.resetToLine(lineId.no)
|
||||
removed.forEach { submittedLines.remove(it.second.linePsi) }
|
||||
@@ -217,12 +232,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,170 +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 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.messages.unescapeLineBreaks
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class ReplFromTerminal(
|
||||
disposable: Disposable,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
replConfiguration: ReplConfiguration
|
||||
) : ReplConfiguration by replConfiguration {
|
||||
private val replInitializer: Future<ReplInterpreter> = Executors.newSingleThreadExecutor().submit(Callable {
|
||||
ReplInterpreter(disposable, compilerConfiguration, replConfiguration)
|
||||
})
|
||||
|
||||
private val replInterpreter: ReplInterpreter
|
||||
get() = replInitializer.get()
|
||||
|
||||
private val messageCollector: 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")
|
||||
|
||||
// Display compiler messages related to configuration and CLI arguments, quit if there are errors
|
||||
val hasErrors = messageCollector.hasErrors()
|
||||
(messageCollector as? GroupingMessageCollector)?.flush()
|
||||
if (hasErrors) return
|
||||
|
||||
var next = WhatNextAfterOneLine.READ_LINE
|
||||
while (true) {
|
||||
next = one(next)
|
||||
if (next == WhatNextAfterOneLine.QUIT) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
errorLogger.logException(e)
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
commandReader.flushHistory()
|
||||
}
|
||||
catch (e: Exception) {
|
||||
errorLogger.logException(e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enum class WhatNextAfterOneLine {
|
||||
READ_LINE,
|
||||
INCOMPLETE,
|
||||
QUIT
|
||||
}
|
||||
|
||||
private fun one(next: WhatNextAfterOneLine): WhatNextAfterOneLine {
|
||||
var line = commandReader.readLine(next) ?: return WhatNextAfterOneLine.QUIT
|
||||
|
||||
line = unescapeLineBreaks(line)
|
||||
|
||||
if (line.startsWith(":") && (line.length == 1 || line.get(1) != ':')) {
|
||||
val notQuit = oneCommand(line.substring(1))
|
||||
return if (notQuit) WhatNextAfterOneLine.READ_LINE else WhatNextAfterOneLine.QUIT
|
||||
}
|
||||
|
||||
val lineResult = eval(line)
|
||||
return if (lineResult is ReplEvalResult.Incomplete) {
|
||||
WhatNextAfterOneLine.INCOMPLETE
|
||||
}
|
||||
else {
|
||||
WhatNextAfterOneLine.READ_LINE
|
||||
}
|
||||
}
|
||||
|
||||
private fun eval(line: String): ReplEvalResult {
|
||||
val evalResult = replInterpreter.eval(line)
|
||||
when (evalResult) {
|
||||
is ReplEvalResult.ValueResult, is ReplEvalResult.UnitResult -> {
|
||||
writer.notifyCommandSuccess()
|
||||
if (evalResult is ReplEvalResult.ValueResult) {
|
||||
writer.outputCommandResult(evalResult.value.toString())
|
||||
}
|
||||
}
|
||||
is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message)
|
||||
is ReplEvalResult.Error.CompileTime -> writer.outputRuntimeError(evalResult.message)
|
||||
is ReplEvalResult.Incomplete -> writer.notifyIncomplete()
|
||||
}
|
||||
return evalResult
|
||||
}
|
||||
|
||||
@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")
|
||||
return true
|
||||
}
|
||||
else if (split.size >= 2 && split[0] == "dump" && split[1] == "bytecode") {
|
||||
replInterpreter.dumpClasses(PrintWriter(System.out))
|
||||
return true
|
||||
}
|
||||
else if (split.size >= 1 && split[0] == "quit") {
|
||||
return false
|
||||
}
|
||||
else if (split.size >= 2 && split[0] == "load") {
|
||||
val fileName = split[1]
|
||||
val scriptText = FileUtil.loadFile(File(fileName))
|
||||
eval(scriptText)
|
||||
return true
|
||||
}
|
||||
else {
|
||||
writer.printlnHelpMessage("Unknown command\n" + "Type :help for help")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun splitCommand(command: String): List<String> {
|
||||
return Arrays.asList(*command.split(" ".toRegex()).dropLastWhile(String::isEmpty).toTypedArray())
|
||||
}
|
||||
|
||||
fun run(disposable: Disposable, configuration: CompilerConfiguration) {
|
||||
val replIdeMode = System.getProperty("kotlin.repl.ideMode") == "true"
|
||||
val replConfiguration = if (replIdeMode) ReplForIdeConfiguration() else ConsoleReplConfiguration()
|
||||
return try {
|
||||
ReplFromTerminal(disposable, configuration, replConfiguration).doRun()
|
||||
}
|
||||
catch (e: Exception) {
|
||||
replConfiguration.errorLogger.logException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.psi.PsiCompiledElement
|
||||
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDescriptorWithExtraFlags
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
@@ -24,19 +27,35 @@ 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.codeinsight.ReplCodeInsightFacadeImpl
|
||||
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.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
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 +67,11 @@ class ReplInterpreter(
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
private val classLoader = ReplClassLoader(URLClassLoader(classpathRoots.map { it.toURI().toURL() }.toTypedArray(), null))
|
||||
|
||||
private val classpathRootsWithSources by lazy {
|
||||
// This is lazy because compiler configuration is not yet set up propertly when the class is initiated
|
||||
classpathRoots.let { it + LibrarySourcesSearcher(configuration).findAdditionalSourceJars(it) }
|
||||
}
|
||||
|
||||
private val messageCollector = object : MessageCollector {
|
||||
private var hasErrors = false
|
||||
@@ -76,57 +99,173 @@ class ReplInterpreter(
|
||||
}
|
||||
|
||||
// TODO: add script definition with project-based resolving for IDEA repl
|
||||
private val scriptCompiler: ReplCompiler by lazy {
|
||||
private val scriptCompiler by lazy {
|
||||
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 docProvider by lazy {
|
||||
LibraryDocInfoProvider(evalState.asState<GenericReplCompilerState>().analyzerEngine.project)
|
||||
}
|
||||
|
||||
private val evalState by lazy { scriptEvaluator.createState() }
|
||||
|
||||
fun eval(line: String): ReplEvalResult {
|
||||
|
||||
val fullText = (previousIncompleteLines + line).joinToString(separator = "\n")
|
||||
val fullText = concatWithPreviousLines(line)
|
||||
|
||||
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 complete(line: String): Collection<DeclarationDescriptor> {
|
||||
val compilerState = evalState.asState<GenericReplCompilerState>()
|
||||
|
||||
val codeFragment = KtPsiFactory(compilerState.analyzerEngine.project).createExpressionCodeFragment(line, null)
|
||||
compilerState.analyzerEngine.setDelegateFactory(codeFragment.containingKtFile)
|
||||
|
||||
val targetIdentifier =
|
||||
codeFragment.findElementAt(codeFragment.textLength - 1)
|
||||
?.getNonStrictParentOfType(KtSimpleNameExpression::class.java)
|
||||
?: return emptyList()
|
||||
|
||||
val codeInsightFacade = ReplCodeInsightFacadeImpl.create(compilerState.analyzerEngine)
|
||||
return codeInsightFacade.complete(targetIdentifier, "o")
|
||||
}
|
||||
|
||||
fun doc(rawFqName: String): String? {
|
||||
val compilerState = evalState.asState<GenericReplCompilerState>()
|
||||
|
||||
val bindingContext = compilerState.analyzerEngine.trace.bindingContext
|
||||
val moduleDescriptor = compilerState.analyzerEngine.module
|
||||
val qualifiedExpressionResolver = compilerState.analyzerEngine.qualifiedExpressionResolver
|
||||
|
||||
val codeFragment = KtPsiFactory(compilerState.analyzerEngine.project).createExpressionCodeFragment(rawFqName, null)
|
||||
compilerState.analyzerEngine.setDelegateFactory(codeFragment.containingKtFile)
|
||||
|
||||
val targetExpression = codeFragment.findElementAt(codeFragment.textLength - 1)?.let {
|
||||
it.getStrictParentOfType<KtQualifiedExpression>()
|
||||
?: it.getNonStrictParentOfType(KtSimpleNameExpression::class.java)
|
||||
} ?: return null
|
||||
|
||||
val scope = compilerState.analyzerEngine.lastSuccessfulLine()?.scopeForInitializerResolution
|
||||
?: LexicalScope.Base(ImportingScope.Empty, moduleDescriptor)
|
||||
|
||||
val descriptors = when (targetExpression) {
|
||||
is KtDotQualifiedExpression -> {
|
||||
val (containingDescriptor, memberName) = qualifiedExpressionResolver
|
||||
.resolveClassOrPackageInQualifiedExpression(targetExpression, scope, bindingContext)
|
||||
|
||||
if (containingDescriptor == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
when (memberName) {
|
||||
null -> listOf(containingDescriptor)
|
||||
else -> {
|
||||
val scopes = when (containingDescriptor) {
|
||||
is PackageViewDescriptor -> listOf(containingDescriptor.memberScope)
|
||||
is PackageFragmentDescriptor -> listOf(containingDescriptor.getMemberScope())
|
||||
is ClassDescriptor -> listOf(containingDescriptor.unsubstitutedMemberScope, containingDescriptor.staticScope)
|
||||
is TypeAliasDescriptor -> listOfNotNull(containingDescriptor.classDescriptor?.unsubstitutedMemberScope)
|
||||
else -> null
|
||||
} ?: return null
|
||||
|
||||
scopes.flatMap { it.getContributedDescriptors(CALLABLES_AND_CLASSIFIERS) { it == memberName } }
|
||||
.filter { it.name == memberName }
|
||||
}
|
||||
}
|
||||
}
|
||||
is KtSimpleNameExpression -> {
|
||||
val a = scope.collectDescriptorsFiltered(
|
||||
CALLABLES_AND_CLASSIFIERS,
|
||||
{ it.asString() == targetExpression.getReferencedName() })
|
||||
a
|
||||
}
|
||||
else -> return null
|
||||
}.takeIf { it.isNotEmpty() } ?: return null
|
||||
|
||||
return descriptors.joinToString(LINE_SEPARATOR + LINE_SEPARATOR) { descriptor ->
|
||||
val fqName = descriptor.fqNameSafe
|
||||
val documentation = findDocumentation(classpathRootsWithSources, descriptor)
|
||||
fqName.asString() + LINE_SEPARATOR + documentation.lines().joinToString(LINE_SEPARATOR) { " " + it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun findDocumentation(libraries: List<File>, descriptor: DeclarationDescriptor): String {
|
||||
if (descriptor is DeclarationDescriptorWithSource) {
|
||||
val psi = descriptor.source.getPsi()
|
||||
|
||||
// REPL declaration
|
||||
if (psi != null && psi !is PsiCompiledElement) {
|
||||
return psi.getChildrenOfType<KDoc>()
|
||||
.flatMap { it.getChildrenOfType<KDocSection>().asList() }
|
||||
.joinToString(LINE_SEPARATOR + LINE_SEPARATOR) { it.text.trim() }
|
||||
}
|
||||
|
||||
// Library declaration
|
||||
val (packageName, declarationName) = splitPackageDeclarationNames(descriptor)
|
||||
|
||||
val language = when (descriptor) {
|
||||
is JavaClassDescriptor, is JvmDescriptorWithExtraFlags, is JavaCallableMemberDescriptor -> JavaLanguage.INSTANCE
|
||||
else -> KotlinLanguage.INSTANCE
|
||||
}
|
||||
|
||||
docProvider.getDoc(language, libraries, packageName, declarationName)?.let { return it }
|
||||
}
|
||||
|
||||
return "No documentation found."
|
||||
}
|
||||
|
||||
private fun splitPackageDeclarationNames(descriptor: DeclarationDescriptor): Pair<String, String> {
|
||||
fun getPackageName(descriptor: DeclarationDescriptor): FqName = when (descriptor) {
|
||||
is PackageFragmentDescriptor -> descriptor.fqName
|
||||
is PackageViewDescriptor -> descriptor.fqName
|
||||
else -> getPackageName(descriptor.containingDeclaration ?: error("$descriptor is outside any package"))
|
||||
}
|
||||
|
||||
val fqName = descriptor.fqNameSafe.asString()
|
||||
val packageName = getPackageName(descriptor).asString()
|
||||
|
||||
if (packageName.isEmpty()) {
|
||||
return Pair("", fqName)
|
||||
}
|
||||
|
||||
assert(fqName.startsWith(packageName + "."))
|
||||
return Pair(packageName, fqName.drop(packageName.length + 1))
|
||||
}
|
||||
|
||||
private fun concatWithPreviousLines(line: String) = (previousIncompleteLines + line).joinToString(separator = "\n")
|
||||
|
||||
fun dumpClasses(out: PrintWriter) {
|
||||
classLoader.dumpClasses(out)
|
||||
scriptEvaluator.classLoader.dumpClasses(out)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val REPL_LINE_AS_SCRIPT_DEFINITION = object : KotlinScriptDefinition(Any::class) {
|
||||
override val name = "Kotlin REPL"
|
||||
}
|
||||
|
||||
private val LINE_SEPARATOR = System.getProperty("line.separator")
|
||||
|
||||
private val CALLABLES_AND_CLASSIFIERS = DescriptorKindFilter(
|
||||
DescriptorKindFilter.CALLABLES_MASK or DescriptorKindFilter.CLASSIFIERS_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.codeinsight
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplClassLoader
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.container.getService
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacadeBase
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import java.io.File
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.Modifier
|
||||
import java.lang.reflect.Proxy.getProxyClass
|
||||
|
||||
interface ReplCodeInsightFacade {
|
||||
fun complete(expression: KtSimpleNameExpression, identifierPart: String): Collection<DeclarationDescriptor>
|
||||
}
|
||||
|
||||
class ReplCodeInsightFacadeImpl(replCodeAnalyzer: ReplCodeAnalyzer) : ReplCodeInsightFacade {
|
||||
companion object {
|
||||
private val CALLABLES_AND_CLASSIFIERS = DescriptorKindFilter(
|
||||
DescriptorKindFilter.CALLABLES_MASK or DescriptorKindFilter.CLASSIFIERS_MASK)
|
||||
|
||||
fun create(replCodeAnalyzer: ReplCodeAnalyzer): ReplCodeInsightFacade {
|
||||
val kotlinCodeInsightFile = getKotlinCodeInsightJar()
|
||||
val classLoaderWithCodeInsight = ReplClassLoader(listOf(kotlinCodeInsightFile), replCodeAnalyzer.javaClass.classLoader)
|
||||
|
||||
fun addClass(className: String) {
|
||||
val bytes = ReplCodeInsightFacadeImpl::class.java.classLoader
|
||||
.getResource(className.replace('.', '/') + ".class").readBytes()
|
||||
|
||||
classLoaderWithCodeInsight.addClass(JvmClassName.byFqNameWithoutInnerClasses(className), bytes)
|
||||
}
|
||||
|
||||
addClass(ReplCodeInsightFacadeImpl::class.java.name)
|
||||
addClass("org.jetbrains.kotlin.cli.jvm.repl.codeinsight.ResolutionFacadeForRepl")
|
||||
|
||||
val actualImplementation = Class.forName(ReplCodeInsightFacadeImpl::class.java.name, true, classLoaderWithCodeInsight)
|
||||
.declaredConstructors.single().newInstance(replCodeAnalyzer)
|
||||
|
||||
val proxyClass = getProxyClass(ReplCodeInsightFacadeImpl::class.java.classLoader, ReplCodeInsightFacade::class.java)
|
||||
val proxy = proxyClass.constructors.single().newInstance(InvocationHandler { _, method, args ->
|
||||
val isStatic = Modifier.isStatic(method.modifiers)
|
||||
|
||||
fun parameterTypesMatches(first: Array<Class<*>>, second: Array<Class<*>>): Boolean {
|
||||
if (first.size != second.size) return false
|
||||
return first.indices.none { first[it] != second[it] }
|
||||
}
|
||||
|
||||
actualImplementation.javaClass.methods
|
||||
.single { it.name == method.name && parameterTypesMatches(it.parameterTypes, method.parameterTypes) }
|
||||
.invoke(if (isStatic) null else actualImplementation, *(args ?: emptyArray()))
|
||||
})
|
||||
|
||||
return proxy as ReplCodeInsightFacade
|
||||
}
|
||||
|
||||
private fun getKotlinCodeInsightJar(): File {
|
||||
System.getProperty("kotlin.code.insight.jar")?.let { return File(it) }
|
||||
|
||||
val kotlinCompilerJarFile = getClasspathEntry(ReplCodeAnalyzer::class.java) ?: error("Can't find Kotlin compiler")
|
||||
|
||||
val kotlinCodeInsightFile = File(kotlinCompilerJarFile.parentFile, "kotlin-code-insight.jar")
|
||||
if (!kotlinCodeInsightFile.exists()) {
|
||||
error("Can't find kotlin-code-insight.jar")
|
||||
}
|
||||
|
||||
return kotlinCodeInsightFile
|
||||
}
|
||||
|
||||
private fun getClasspathEntry(clazz: Class<*>): File? {
|
||||
val classFileName = clazz.name.replace('.', '/') + ".class"
|
||||
val resource = clazz.classLoader.getResource(classFileName) ?: return null
|
||||
|
||||
return File(
|
||||
when (resource.protocol?.toLowerCase()) {
|
||||
"file" -> resource.path
|
||||
?.takeIf { it.endsWith(classFileName) }
|
||||
?.removeSuffix(classFileName)
|
||||
"jar" -> resource.path
|
||||
.takeIf { it.startsWith("file:", ignoreCase = true) }
|
||||
?.drop("file:".length)
|
||||
?.substringBefore("!/")
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val referenceVariantsHelper = ReferenceVariantsHelper(
|
||||
replCodeAnalyzer.trace.bindingContext,
|
||||
ResolutionFacadeForRepl(replCodeAnalyzer),
|
||||
replCodeAnalyzer.module)
|
||||
|
||||
override fun complete(expression: KtSimpleNameExpression, identifierPart: String): Collection<DeclarationDescriptor> {
|
||||
// TODO do not filter out
|
||||
return referenceVariantsHelper.getReferenceVariants(expression, CALLABLES_AND_CLASSIFIERS, NameFilter(identifierPart))
|
||||
}
|
||||
|
||||
class NameFilter(val prefix: String) : (Name) -> Boolean {
|
||||
override fun invoke(name: Name): Boolean {
|
||||
return !name.isSpecial && applicableNameFor(prefix, name.identifier)
|
||||
}
|
||||
|
||||
private fun applicableNameFor(prefix: String, completion: String): Boolean {
|
||||
return completion.startsWith(prefix) || completion.toLowerCase().startsWith(prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ResolutionFacadeForRepl(private val replCodeAnalyzer: ReplCodeAnalyzer) : ResolutionFacadeBase {
|
||||
override val project: Project
|
||||
get() = replCodeAnalyzer.project
|
||||
|
||||
override val moduleDescriptor: ModuleDescriptor
|
||||
get() = replCodeAnalyzer.module
|
||||
|
||||
override fun <T : Any> getFrontendService(serviceClass: Class<T>): T {
|
||||
return replCodeAnalyzer.componentProvider.getService(serviceClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.command
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand.*
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandContext
|
||||
import java.io.PrintWriter
|
||||
|
||||
object ClassesReplCommand : ReplCommand {
|
||||
override val name = "classes"
|
||||
override val args = emptyList<String>()
|
||||
override val help = "dump loaded classes"
|
||||
|
||||
override val constraints = ArgumentConstraints.Empty
|
||||
|
||||
override fun run(context: ReplCommandContext, rawArgs: String): Boolean {
|
||||
context.interpreter.dumpClasses(PrintWriter(System.out))
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.command
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand.*
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandContext
|
||||
|
||||
object DocReplCommand : ReplCommand {
|
||||
override val name = "doc"
|
||||
override val args = listOf("symbol")
|
||||
override val help = "display documentation for the given symbol"
|
||||
|
||||
override val constraints = ArgumentConstraints.Raw
|
||||
|
||||
override fun run(context: ReplCommandContext, rawArgs: String): Boolean {
|
||||
val docComment = context.interpreter.doc(rawArgs)
|
||||
context.configuration.writer.printlnHelpMessage(docComment ?: "No documentation found.")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.command
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand.*
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandContext
|
||||
|
||||
object HelpReplCommand : ReplCommand {
|
||||
override val name = "help"
|
||||
override val args = emptyList<String>()
|
||||
override val help = "display help message"
|
||||
|
||||
override val constraints = ArgumentConstraints.Empty
|
||||
|
||||
override fun run(context: ReplCommandContext, rawArgs: String): Boolean {
|
||||
val commands = ReplCommand.COMMANDS.map { (_, command) ->
|
||||
buildString {
|
||||
append(command.name)
|
||||
if (command.args.isNotEmpty()) {
|
||||
append(' ')
|
||||
command.args.joinTo(this) { "<$it>" }
|
||||
}
|
||||
} to command.help
|
||||
}
|
||||
|
||||
val maxCommandLength = (commands.map { it.first.length }.max() ?: 0) + 4
|
||||
val renderedCommands = commands
|
||||
.joinToString("\n") { ":" + it.first + " ".repeat(maxCommandLength - it.first.length) + it.second }
|
||||
|
||||
context.configuration.writer.printlnHelpMessage("Available commands:\n" + renderedCommands)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.command
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand.*
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandContext
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
object LoadReplCommand : ReplCommand {
|
||||
override val name = "load"
|
||||
override val args = listOf("file")
|
||||
override val help = "evaluate a script file"
|
||||
|
||||
override val constraints = ArgumentConstraints.Raw
|
||||
|
||||
override fun run(context: ReplCommandContext, rawArgs: String): Boolean {
|
||||
fun printError(text: String) = context.configuration.writer.outputCompileError(text)
|
||||
|
||||
val file = File(rawArgs).takeIf { it.isFile } ?: run {
|
||||
printError("File not exists: ${File(rawArgs).absolutePath}")
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
val scriptText = FileUtil.loadFile(file)
|
||||
context.interpreter.eval(scriptText)
|
||||
} catch (e: IOException) {
|
||||
printError("Can not load script: ${e.message}")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.command
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommand.*
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandContext
|
||||
|
||||
object QuitReplCommand : ReplCommand {
|
||||
override val name = "quit"
|
||||
override val args = emptyList<String>()
|
||||
override val help = "quit REPL"
|
||||
|
||||
override val constraints = ArgumentConstraints.Empty
|
||||
|
||||
override fun run(context: ReplCommandContext, rawArgs: String): Boolean {
|
||||
if (rawArgs.isNotEmpty()) {
|
||||
context.configuration.writer.outputCompileError("Expected zero arguments, got $rawArgs")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -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.ConsoleReplReader
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.writer.ConsoleReplWriter
|
||||
|
||||
class ConsoleReplConfiguration : ReplConfiguration {
|
||||
override val writer = ConsoleReplWriter()
|
||||
|
||||
override val exceptionReporter
|
||||
get() = ReplExceptionReporter
|
||||
|
||||
override val reader = ConsoleReplReader()
|
||||
|
||||
override val allowIncompleteLines: Boolean
|
||||
get() = true
|
||||
|
||||
override val executionInterceptor
|
||||
get() = SnippetExecutionInterceptor
|
||||
|
||||
override fun createDiagnosticHolder() = ConsoleDiagnosticMessageHolder()
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.*
|
||||
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 reader: ReplReader
|
||||
|
||||
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)
|
||||
reader = IdeReplReader()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.ReplReader
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.writer.ReplWriter
|
||||
|
||||
interface ReplConfiguration {
|
||||
val reader: ReplReader
|
||||
val writer: ReplWriter
|
||||
val exceptionReporter: ReplExceptionReporter
|
||||
|
||||
val allowIncompleteLines: Boolean
|
||||
|
||||
val executionInterceptor: SnippetExecutionInterceptor
|
||||
fun createDiagnosticHolder(): DiagnosticMessageHolder
|
||||
}
|
||||
@@ -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,11 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.reader
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.configuration
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
||||
interface SnippetExecutionInterceptor {
|
||||
fun <T> execute(block: () -> T): T
|
||||
|
||||
interface ReplCommandReader {
|
||||
fun readLine(next: ReplFromTerminal.WhatNextAfterOneLine): String?
|
||||
fun flushHistory()
|
||||
companion object Plain : SnippetExecutionInterceptor {
|
||||
override fun <T> execute(block: () -> T) = block()
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -1,61 +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.reader
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
||||
import org.jline.reader.EndOfFileException
|
||||
import org.jline.reader.LineReader
|
||||
import org.jline.reader.LineReaderBuilder
|
||||
import org.jline.reader.UserInterruptException
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
|
||||
class ConsoleReplCommandReader : ReplCommandReader {
|
||||
private val lineReader = LineReaderBuilder.builder()
|
||||
.appName("kotlin")
|
||||
.terminal(TerminalBuilder.terminal())
|
||||
.variable(LineReader.HISTORY_FILE, File(File(System.getProperty("user.home")), ".kotlinc_history").absolutePath)
|
||||
.build()
|
||||
.apply {
|
||||
setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION)
|
||||
}
|
||||
|
||||
override fun readLine(next: ReplFromTerminal.WhatNextAfterOneLine): String? {
|
||||
val prompt = if (next == ReplFromTerminal.WhatNextAfterOneLine.INCOMPLETE) "... " else ">>> "
|
||||
try {
|
||||
return lineReader.readLine(prompt)
|
||||
}
|
||||
catch (e: UserInterruptException) {
|
||||
println("<interrupted>")
|
||||
System.out.flush()
|
||||
return ""
|
||||
}
|
||||
catch (e: EndOfFileException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun flushHistory() = lineReader.history.save()
|
||||
|
||||
private companion object {
|
||||
init {
|
||||
Logger.getLogger("org.jline").level = Level.OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.reader
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.KotlinRepl
|
||||
import org.jline.reader.*
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
|
||||
class ConsoleReplReader : ReplReader {
|
||||
private var processor: ReplProcessor? = null
|
||||
|
||||
private val lineReader = LineReaderBuilder.builder()
|
||||
.appName("kotlin")
|
||||
.terminal(TerminalBuilder.terminal())
|
||||
.variable(LineReader.HISTORY_FILE, File(File(System.getProperty("user.home")), ".kotlinc_history").absolutePath)
|
||||
.completer(::completer)
|
||||
.build()
|
||||
.apply {
|
||||
Logger.getLogger("org.jline").level = Level.OFF
|
||||
setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION)
|
||||
}
|
||||
|
||||
override fun runLoop(processor: ReplProcessor) {
|
||||
try {
|
||||
this.processor = processor
|
||||
super.runLoop(processor)
|
||||
} finally {
|
||||
this.processor = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun completer(lineReader: LineReader, parsedLine: ParsedLine, candidates: MutableList<Candidate>) {
|
||||
val processor = this.processor ?: error("ReplProcessor is not initialized")
|
||||
candidates.addAll(processor.complete(parsedLine.line(), parsedLine.cursor()).map { Candidate(it) })
|
||||
}
|
||||
|
||||
override fun readLine(next: KotlinRepl.WhatNextAfterOneLine): String? {
|
||||
val prompt = if (next == KotlinRepl.WhatNextAfterOneLine.INCOMPLETE) "... " else ">>> "
|
||||
|
||||
return try {
|
||||
lineReader.readLine(prompt)
|
||||
} catch (e: UserInterruptException) {
|
||||
println("<interrupted>")
|
||||
System.out.flush()
|
||||
""
|
||||
} catch (e: EndOfFileException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun flushHistory() = lineReader.history.save()
|
||||
}
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.reader
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.KotlinRepl
|
||||
|
||||
class IdeReplCommandReader : ReplCommandReader {
|
||||
override fun readLine(next: ReplFromTerminal.WhatNextAfterOneLine) = readLine()
|
||||
class IdeReplReader : ReplReader {
|
||||
override fun readLine(next: KotlinRepl.WhatNextAfterOneLine) = readLine()
|
||||
override fun flushHistory() = Unit
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.repl.reader
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplInterpreter
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.command.*
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.configuration.ReplConfiguration
|
||||
|
||||
class ReplProcessor(
|
||||
interpreter: ReplInterpreter,
|
||||
configuration: ReplConfiguration
|
||||
) {
|
||||
private val context = ReplCommandContext(interpreter, configuration)
|
||||
|
||||
fun processCommand(commandName: String, rawArgs: String): Boolean {
|
||||
val command = ReplCommand.COMMANDS[commandName] ?: run {
|
||||
context.configuration.writer.printlnHelpMessage("Unknown command\n" + "Type :help for help")
|
||||
return true
|
||||
}
|
||||
|
||||
return command.run(context, rawArgs)
|
||||
}
|
||||
|
||||
fun evaluate(text: String): ReplEvalResult {
|
||||
val evalResult = context.interpreter.eval(text)
|
||||
val writer = context.configuration.writer
|
||||
|
||||
when (evalResult) {
|
||||
is ReplEvalResult.ValueResult, is ReplEvalResult.UnitResult -> {
|
||||
writer.notifyCommandSuccess()
|
||||
if (evalResult is ReplEvalResult.ValueResult) {
|
||||
writer.outputCommandResult(evalResult.value.toString())
|
||||
}
|
||||
}
|
||||
is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message)
|
||||
is ReplEvalResult.Error.CompileTime -> writer.outputRuntimeError(evalResult.message)
|
||||
is ReplEvalResult.Incomplete -> writer.notifyIncomplete()
|
||||
}
|
||||
return evalResult
|
||||
}
|
||||
|
||||
fun complete(line: String, position: Int): List<String> {
|
||||
return context.interpreter.complete(line.substring(0, position)).map { it.name.asString() }
|
||||
}
|
||||
}
|
||||
|
||||
interface ReplCommand {
|
||||
val name: String
|
||||
val args: List<String>
|
||||
val help: String
|
||||
|
||||
val constraints: ArgumentConstraints
|
||||
|
||||
/** Returns `true` if REPL should continue processing the next line. */
|
||||
fun run(context: ReplCommandContext, rawArgs: String): Boolean
|
||||
|
||||
sealed class ArgumentConstraints {
|
||||
object Empty : ArgumentConstraints()
|
||||
object Raw : ArgumentConstraints()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val COMMANDS: Map<String, ReplCommand> = listOf(
|
||||
ClassesReplCommand,
|
||||
DocReplCommand,
|
||||
LoadReplCommand,
|
||||
HelpReplCommand,
|
||||
QuitReplCommand
|
||||
).map { it.name to it }.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
class ReplCommandContext(val interpreter: ReplInterpreter, val configuration: ReplConfiguration)
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.reader
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.KotlinRepl
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.unescapeLineBreaks
|
||||
|
||||
interface ReplReader {
|
||||
fun readLine(next: KotlinRepl.WhatNextAfterOneLine): String?
|
||||
fun flushHistory()
|
||||
|
||||
tailrec fun runLoop(processor: ReplProcessor) {
|
||||
val nextLine = KotlinRepl.WhatNextAfterOneLine.READ_LINE
|
||||
if (processLine(processor, nextLine) != KotlinRepl.WhatNextAfterOneLine.QUIT) {
|
||||
runLoop(processor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processLine(
|
||||
processor: ReplProcessor,
|
||||
next: KotlinRepl.WhatNextAfterOneLine
|
||||
): KotlinRepl.WhatNextAfterOneLine {
|
||||
val rawLine = readLine(next) ?: return KotlinRepl.WhatNextAfterOneLine.QUIT
|
||||
val line = unescapeLineBreaks(rawLine)
|
||||
|
||||
if (isCommand(line)) {
|
||||
val commandName = line.substringBefore(' ')
|
||||
val rawArgs = line.substringAfter(' ', missingDelimiterValue = "").trim()
|
||||
|
||||
return if (processor.processCommand(commandName.drop(1), rawArgs)) {
|
||||
KotlinRepl.WhatNextAfterOneLine.READ_LINE
|
||||
} else {
|
||||
KotlinRepl.WhatNextAfterOneLine.QUIT
|
||||
}
|
||||
}
|
||||
|
||||
val lineResult = processor.evaluate(line)
|
||||
return if (lineResult is ReplEvalResult.Incomplete) {
|
||||
KotlinRepl.WhatNextAfterOneLine.INCOMPLETE
|
||||
} else {
|
||||
KotlinRepl.WhatNextAfterOneLine.READ_LINE
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCommand(line: String) = line.startsWith(":") && (line.length == 1 || line[1] != ':')
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -64,8 +64,8 @@ open class KotlinRemoteReplCompilerClient(
|
||||
RemoteReplCompilerState(compileService.replCreateState(sessionId).get(), lock)
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult =
|
||||
compileService.replCheck(sessionId, state.asState(RemoteReplCompilerState::class.java).replStateFacade.getId(), codeLine).get()
|
||||
compileService.replCheck(sessionId, state.asState<RemoteReplCompilerState>().replStateFacade.getId(), codeLine).get()
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult =
|
||||
compileService.replCompile(sessionId, state.asState(RemoteReplCompilerState::class.java).replStateFacade.getId(), codeLine).get()
|
||||
compileService.replCompile(sessionId, state.asState<RemoteReplCompilerState>().replStateFacade.getId(), codeLine).get()
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ open class KotlinJvmReplService(
|
||||
|
||||
fun createRemoteState(port: Int = portForServers): RemoteReplStateFacadeServer = statesLock.write {
|
||||
val id = getValidId(stateIdCounter) { id -> states.none { it.key.getId() == id} }
|
||||
val stateFacade = RemoteReplStateFacadeServer(id, createState().asState(GenericReplCompilerState::class.java), port)
|
||||
val stateFacade = RemoteReplStateFacadeServer(id, createState().asState(), port)
|
||||
states.put(stateFacade, true)
|
||||
stateFacade
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
^
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
public interface ScriptDescriptor extends ClassDescriptor {
|
||||
int getPriority();
|
||||
|
||||
boolean isReplSnippet();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
ClassConstructorDescriptor getUnsubstitutedPrimaryConstructor();
|
||||
|
||||
@@ -16,3 +16,4 @@ sourceSets {
|
||||
"test" {}
|
||||
}
|
||||
|
||||
dist(targetName = "kotlin-code-insight.jar")
|
||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.codeInsight
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacadeBase
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
@@ -49,9 +49,9 @@ import java.util.*
|
||||
|
||||
class ReferenceVariantsHelper(
|
||||
private val bindingContext: BindingContext,
|
||||
private val resolutionFacade: ResolutionFacade,
|
||||
private val resolutionFacade: ResolutionFacadeBase,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean = { _ -> true },
|
||||
private val notProperties: Set<FqNameUnsafe> = setOf()
|
||||
) {
|
||||
fun getReferenceVariants(
|
||||
@@ -422,7 +422,7 @@ class ReferenceVariantsHelper(
|
||||
}
|
||||
|
||||
private fun MemberScope.collectStaticMembers(
|
||||
resolutionFacade: ResolutionFacade,
|
||||
resolutionFacade: ResolutionFacadeBase,
|
||||
kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean
|
||||
): Collection<DeclarationDescriptor> {
|
||||
@@ -430,7 +430,7 @@ private fun MemberScope.collectStaticMembers(
|
||||
}
|
||||
|
||||
fun ResolutionScope.collectSyntheticStaticMembersAndConstructors(
|
||||
resolutionFacade: ResolutionFacade,
|
||||
resolutionFacade: ResolutionFacadeBase,
|
||||
kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean
|
||||
): List<FunctionDescriptor> {
|
||||
|
||||
@@ -26,9 +26,22 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
interface ResolutionFacade {
|
||||
interface ResolutionFacadeBase {
|
||||
val project: Project
|
||||
|
||||
val moduleDescriptor: ModuleDescriptor
|
||||
|
||||
// get service for the module this resolution was created for
|
||||
fun <T : Any> getFrontendService(serviceClass: Class<T>): T
|
||||
|
||||
// get service for the module defined by PsiElement/ModuleDescriptor passed as parameter
|
||||
fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T
|
||||
fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T?
|
||||
|
||||
fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T
|
||||
}
|
||||
|
||||
interface ResolutionFacade : ResolutionFacadeBase {
|
||||
fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext
|
||||
fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext
|
||||
|
||||
@@ -36,22 +49,10 @@ interface ResolutionFacade {
|
||||
|
||||
fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): DeclarationDescriptor
|
||||
|
||||
val moduleDescriptor: ModuleDescriptor
|
||||
|
||||
// get service for the module this resolution was created for
|
||||
fun <T : Any> getFrontendService(serviceClass: Class<T>): T
|
||||
|
||||
fun <T : Any> getIdeService(serviceClass: Class<T>): T
|
||||
|
||||
// get service for the module defined by PsiElement/ModuleDescriptor passed as parameter
|
||||
fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T
|
||||
fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T?
|
||||
|
||||
fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> ResolutionFacade.frontendService(): T
|
||||
inline fun <reified T : Any> ResolutionFacadeBase.frontendService(): T
|
||||
= this.getFrontendService(T::class.java)
|
||||
|
||||
inline fun <reified T : Any> ResolutionFacade.ideService(): T
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.util
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacadeBase
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -219,7 +219,7 @@ fun CallTypeAndReceiver<*, *>.receiverTypes(
|
||||
bindingContext: BindingContext,
|
||||
contextElement: PsiElement,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
resolutionFacade: ResolutionFacadeBase,
|
||||
stableSmartCastsOnly: Boolean
|
||||
): Collection<KotlinType>? {
|
||||
return receiverTypesWithIndex(bindingContext, contextElement, moduleDescriptor, resolutionFacade, stableSmartCastsOnly)?.map { it.type }
|
||||
@@ -229,7 +229,7 @@ fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
|
||||
bindingContext: BindingContext,
|
||||
contextElement: PsiElement,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
resolutionFacade: ResolutionFacadeBase,
|
||||
stableSmartCastsOnly: Boolean,
|
||||
withImplicitReceiversWhenExplicitPresent: Boolean = false
|
||||
): Collection<ReceiverType>? {
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
package org.jetbrains.kotlin.idea.util
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacadeBase
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -40,14 +39,14 @@ import java.util.*
|
||||
|
||||
class ShadowedDeclarationsFilter(
|
||||
private val bindingContext: BindingContext,
|
||||
private val resolutionFacade: ResolutionFacade,
|
||||
private val resolutionFacade: ResolutionFacadeBase,
|
||||
private val context: PsiElement,
|
||||
private val explicitReceiverValue: ReceiverValue?
|
||||
) {
|
||||
companion object {
|
||||
fun create(
|
||||
bindingContext: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
resolutionFacade: ResolutionFacadeBase,
|
||||
context: PsiElement,
|
||||
callTypeAndReceiver: CallTypeAndReceiver<*, *>
|
||||
): ShadowedDeclarationsFilter? {
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacadeBase
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -80,7 +80,7 @@ fun PsiElement.getResolutionScope(bindingContext: BindingContext): LexicalScope?
|
||||
return null
|
||||
}
|
||||
|
||||
fun PsiElement.getResolutionScope(bindingContext: BindingContext, resolutionFacade: ResolutionFacade/*TODO: get rid of this parameter*/): LexicalScope {
|
||||
fun PsiElement.getResolutionScope(bindingContext: BindingContext, resolutionFacade: ResolutionFacadeBase/*TODO: get rid of this parameter*/): LexicalScope {
|
||||
return getResolutionScope(bindingContext) ?:
|
||||
when (containingFile) {
|
||||
is KtFile -> resolutionFacade.getFileResolutionScope(containingFile as KtFile)
|
||||
@@ -94,6 +94,6 @@ fun KtElement.getResolutionScope(): LexicalScope {
|
||||
return getResolutionScope(context, resolutionFacade)
|
||||
}
|
||||
|
||||
fun ResolutionFacade.getFileResolutionScope(file: KtFile): LexicalScope {
|
||||
fun ResolutionFacadeBase.getFileResolutionScope(file: KtFile): LexicalScope {
|
||||
return frontendService<FileScopeProvider>().getFileResolutionScope(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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user