private KotlinTypeInfo getTypeOfLastExpressionInBlock( @NotNull KtExpression statementExpression, @NotNull ExpressionTypingContext context, @NotNull CoercionStrategy coercionStrategyForLastExpression, @NotNull ExpressionTypingInternals blockLevelVisitor) { if (context.expectedType != NO_EXPECTED_TYPE) { KotlinType expectedType; if (context.expectedType == UNIT_EXPECTED_TYPE || // the first check is necessary to avoid invocation 'isUnit(UNIT_EXPECTED_TYPE)' (coercionStrategyForLastExpression == COERCION_TO_UNIT && KotlinBuiltIns.isUnit(context.expectedType))) { expectedType = UNIT_EXPECTED_TYPE; } else { expectedType = context.expectedType; } return blockLevelVisitor.getTypeInfo( statementExpression, context.replaceExpectedType(expectedType), true); } KotlinTypeInfo result = blockLevelVisitor.getTypeInfo(statementExpression, context, true); if (coercionStrategyForLastExpression == COERCION_TO_UNIT) { boolean mightBeUnit = false; if (statementExpression instanceof KtDeclaration) { mightBeUnit = true; } if (statementExpression instanceof KtBinaryExpression) { KtBinaryExpression binaryExpression = (KtBinaryExpression) statementExpression; IElementType operationType = binaryExpression.getOperationToken(); //noinspection SuspiciousMethodCalls if (operationType == KtTokens.EQ || OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) { mightBeUnit = true; } } if (mightBeUnit) { // ExpressionTypingVisitorForStatements should return only null or Unit for declarations and // assignments, // but (for correct assignment / initialization analysis) data flow info must be preserved assert result.getType() == null || KotlinBuiltIns.isUnit(result.getType()); result = result.replaceType(expressionTypingComponents.builtIns.getUnitType()); } } return result; }
@Override public KotlinTypeInfo visitReturnExpression( @NotNull KtReturnExpression expression, ExpressionTypingContext context) { KtElement labelTargetElement = LabelResolver.INSTANCE.resolveControlLabel(expression, context); KtExpression returnedExpression = expression.getReturnedExpression(); KotlinType expectedType = NO_EXPECTED_TYPE; KotlinType resultType = components.builtIns.getNothingType(); KtDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(expression, KtDeclaration.class); if (parentDeclaration instanceof KtParameter) { // In a default value for parameter context.trace.report(RETURN_NOT_ALLOWED.on(expression)); } if (expression.getTargetLabel() == null) { while (parentDeclaration instanceof KtMultiDeclaration) { // TODO: It's hacking fix for KT-5100: Strange "Return is not allowed here" for // multi-declaration initializer with elvis expression parentDeclaration = PsiTreeUtil.getParentOfType(parentDeclaration, KtDeclaration.class); } assert parentDeclaration != null : "Can't find parent declaration for " + expression.getText(); DeclarationDescriptor declarationDescriptor = context.trace.get(DECLARATION_TO_DESCRIPTOR, parentDeclaration); Pair<FunctionDescriptor, PsiElement> containingFunInfo = BindingContextUtils.getContainingFunctionSkipFunctionLiterals( declarationDescriptor, false); FunctionDescriptor containingFunctionDescriptor = containingFunInfo.getFirst(); if (containingFunctionDescriptor != null) { if (!InlineUtil.checkNonLocalReturnUsage( containingFunctionDescriptor, expression, context.trace) || isClassInitializer(containingFunInfo)) { // Unqualified, in a function literal context.trace.report(RETURN_NOT_ALLOWED.on(expression)); resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); } expectedType = getFunctionExpectedReturnType( containingFunctionDescriptor, (KtElement) containingFunInfo.getSecond(), context); } else { // Outside a function context.trace.report(RETURN_NOT_ALLOWED.on(expression)); resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); } } else if (labelTargetElement != null) { SimpleFunctionDescriptor functionDescriptor = context.trace.get(FUNCTION, labelTargetElement); if (functionDescriptor != null) { expectedType = getFunctionExpectedReturnType(functionDescriptor, labelTargetElement, context); if (!InlineUtil.checkNonLocalReturnUsage(functionDescriptor, expression, context.trace)) { // Qualified, non-local context.trace.report(RETURN_NOT_ALLOWED.on(expression)); resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); } } else { context.trace.report(NOT_A_RETURN_LABEL.on(expression, expression.getLabelName())); } } if (returnedExpression != null) { facade.getTypeInfo( returnedExpression, context .replaceExpectedType(expectedType) .replaceScope(context.scope) .replaceContextDependency(INDEPENDENT)); } else { if (expectedType != null && !noExpectedType(expectedType) && !KotlinBuiltIns.isUnit(expectedType) && !isDontCarePlaceholder(expectedType)) // for lambda with implicit return type Unit { context.trace.report(RETURN_TYPE_MISMATCH.on(expression, expectedType)); } } return components.dataFlowAnalyzer.createCheckedTypeInfo(resultType, context, expression); }
@NotNull public LineResult eval(@NotNull String line) { ++lineNumber; FqName scriptFqName = new FqName("Line" + lineNumber); Type scriptClassType = asmTypeByFqNameWithoutInnerClasses(scriptFqName); StringBuilder fullText = new StringBuilder(); for (String prevLine : previousIncompleteLines) { fullText.append(prevLine).append("\n"); } fullText.append(line); LightVirtualFile virtualFile = new LightVirtualFile( "line" + lineNumber + JetParserDefinition.STD_SCRIPT_EXT, JetLanguage.INSTANCE, fullText.toString()); virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); JetFile psiFile = (JetFile) psiFileFactory.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); assert psiFile != null : "Script file not analyzed at line " + lineNumber + ": " + fullText; ReplMessageCollectorWrapper errorCollector = new ReplMessageCollectorWrapper(); AnalyzerWithCompilerReport.SyntaxErrorReport syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors( psiFile, errorCollector.getMessageCollector()); if (syntaxErrorReport.isHasErrors() && syntaxErrorReport.isAllErrorsAtEof()) { previousIncompleteLines.add(line); return LineResult.incomplete(); } previousIncompleteLines.clear(); if (syntaxErrorReport.isHasErrors()) { return LineResult.error(errorCollector.getString()); } prepareForTheNextReplLine(topDownAnalysisContext); trace.clearDiagnostics(); //noinspection ConstantConditions psiFile.getScript().putUserData(ScriptPriorities.PRIORITY_KEY, lineNumber); ScriptDescriptor scriptDescriptor = doAnalyze(psiFile, errorCollector); if (scriptDescriptor == null) { return LineResult.error(errorCollector.getString()); } List<Pair<ScriptDescriptor, Type>> earlierScripts = Lists.newArrayList(); for (EarlierLine earlierLine : earlierLines) { earlierScripts.add( Pair.create(earlierLine.getScriptDescriptor(), earlierLine.getClassType())); } GenerationState state = new GenerationState( psiFile.getProject(), ClassBuilderFactories.BINARIES, module, trace.getBindingContext(), Collections.singletonList(psiFile)); compileScript( psiFile.getScript(), scriptClassType, earlierScripts, state, CompilationErrorHandler.THROW_EXCEPTION); for (OutputFile outputFile : state.getFactory().asList()) { classLoader.addClass( JvmClassName.byInternalName(outputFile.getRelativePath().replaceFirst("\\.class$", "")), outputFile.asByteArray()); } try { Class<?> scriptClass = classLoader.loadClass(scriptFqName.asString()); Class<?>[] constructorParams = new Class<?>[earlierLines.size()]; Object[] constructorArgs = new Object[earlierLines.size()]; for (int i = 0; i < earlierLines.size(); ++i) { constructorParams[i] = earlierLines.get(i).getScriptClass(); constructorArgs[i] = earlierLines.get(i).getScriptInstance(); } Constructor<?> scriptInstanceConstructor = scriptClass.getConstructor(constructorParams); Object scriptInstance; try { scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs); } catch (Throwable e) { return LineResult.error(renderStackTrace(e.getCause())); } Field rvField = scriptClass.getDeclaredField("rv"); rvField.setAccessible(true); Object rv = rvField.get(scriptInstance); earlierLines.add( new EarlierLine(line, scriptDescriptor, scriptClass, scriptInstance, scriptClassType)); JetType returnType = scriptDescriptor.getScriptCodeDescriptor().getReturnType(); return LineResult.successful(rv, returnType != null && KotlinBuiltIns.isUnit(returnType)); } catch (Throwable e) { @SuppressWarnings("UseOfSystemOutOrSystemErr") PrintWriter writer = new PrintWriter(System.err); classLoader.dumpClasses(writer); writer.flush(); throw UtilsPackage.rethrow(e); } }