[JS IR] Implemented a set of JS code checks before klib serialization
The diagnostics cannot be implemented with the FIR frontend checker because it requires constant evaluation over FIR. Therefore, the diagnostics are implemented as a set of klib checks over IR. For the diagnostics, the js() call argument must be evaluated and inlined as IrConst<String> into IR in the same way as const val initializers and annotation arguments. ^KT-59388 Fixed ^KT-59399 Fixed ^KT-62425 Fixed
This commit is contained in:
committed by
Space Team
parent
71eaf651e8
commit
629e0628d6
@@ -6,6 +6,8 @@ plugins {
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:ir.tree"))
|
||||
compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
|
||||
|
||||
implementation(project(":core:compiler.common.js"))
|
||||
}
|
||||
|
||||
optInToUnsafeDuringIrConstructionAPI()
|
||||
|
||||
+4
-4
@@ -42,14 +42,14 @@ internal abstract class IrConstExpressionTransformer(
|
||||
|
||||
override fun visitClass(declaration: IrClass, data: Data): IrStatement {
|
||||
if (declaration.kind == ClassKind.ANNOTATION_CLASS) {
|
||||
return super.visitClass(declaration, data.copy(inAnnotation = true))
|
||||
return super.visitClass(declaration, data.copy(inConstantExpression = true))
|
||||
}
|
||||
return super.visitClass(declaration, data)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, data: Data): IrElement {
|
||||
if (expression.canBeInterpreted()) {
|
||||
return expression.interpret(failAsError = data.inAnnotation)
|
||||
return expression.interpret(failAsError = data.inConstantExpression)
|
||||
}
|
||||
return super.visitCall(expression, data)
|
||||
}
|
||||
@@ -68,7 +68,7 @@ internal abstract class IrConstExpressionTransformer(
|
||||
|
||||
override fun visitGetField(expression: IrGetField, data: Data): IrExpression {
|
||||
if (expression.canBeInterpreted()) {
|
||||
return expression.interpret(failAsError = data.inAnnotation)
|
||||
return expression.interpret(failAsError = data.inConstantExpression)
|
||||
}
|
||||
return super.visitGetField(expression, data)
|
||||
}
|
||||
@@ -78,7 +78,7 @@ internal abstract class IrConstExpressionTransformer(
|
||||
this.startOffset, this.endOffset, expression.type, listOf(this@wrapInStringConcat)
|
||||
)
|
||||
|
||||
fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = data.inAnnotation)
|
||||
fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = data.inConstantExpression)
|
||||
fun IrExpression.getConstStringOrEmpty(): String = if (this is IrConst<*>) value.toString() else ""
|
||||
|
||||
// If we have some complex expression in arguments (like some `IrComposite`) we will skip it,
|
||||
|
||||
+12
-6
@@ -11,16 +11,19 @@ import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
||||
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
|
||||
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterChecker
|
||||
import org.jetbrains.kotlin.ir.interpreter.isConst
|
||||
import org.jetbrains.kotlin.ir.interpreter.property
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.name.JsStandardClassIds
|
||||
|
||||
/**
|
||||
* This transformer will visit all expressions and will evaluate only those that are necessary. By "necessary" we mean expressions
|
||||
* that are used in `const val` and inside annotations.
|
||||
* This transformer will visit all expressions and will evaluate only those that are necessary.
|
||||
* By "necessary" we mean expressions that are used in `const val`, inside annotations and js() call arguments.
|
||||
*/
|
||||
internal class IrConstOnlyNecessaryTransformer(
|
||||
interpreter: IrInterpreter,
|
||||
@@ -35,10 +38,13 @@ internal class IrConstOnlyNecessaryTransformer(
|
||||
) : IrConstExpressionTransformer(
|
||||
interpreter, irFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
|
||||
) {
|
||||
private val jsCodeFqName = JsStandardClassIds.Callables.JsCode.asSingleFqName()
|
||||
|
||||
override fun visitCall(expression: IrCall, data: Data): IrElement {
|
||||
val isConstGetter = expression.symbol.owner.property.isConst
|
||||
if (!data.inAnnotation && !isConstGetter) {
|
||||
expression.transformChildren(this, data)
|
||||
val isJsCodeCall = expression.symbol.owner.fqNameWhenAvailable == jsCodeFqName
|
||||
if (isJsCodeCall || (!data.inConstantExpression && !isConstGetter)) {
|
||||
expression.transformChildren(this, data.copy(inConstantExpression = data.inConstantExpression || isJsCodeCall))
|
||||
return expression
|
||||
}
|
||||
return super.visitCall(expression, data)
|
||||
@@ -46,12 +52,12 @@ internal class IrConstOnlyNecessaryTransformer(
|
||||
|
||||
override fun visitGetField(expression: IrGetField, data: Data): IrExpression {
|
||||
val isConst = expression.symbol.owner.property.isConst
|
||||
if (!data.inAnnotation && !isConst) return expression
|
||||
if (!data.inConstantExpression && !isConst) return expression
|
||||
return super.visitGetField(expression, data)
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Data): IrExpression {
|
||||
if (!data.inAnnotation) {
|
||||
if (!data.inConstantExpression) {
|
||||
expression.transformChildren(this, data)
|
||||
return expression
|
||||
}
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ internal abstract class IrConstTransformer(
|
||||
private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
|
||||
private val suppressExceptions: Boolean,
|
||||
) : IrElementTransformer<IrConstTransformer.Data> {
|
||||
internal data class Data(val inAnnotation: Boolean = false)
|
||||
internal data class Data(val inConstantExpression: Boolean = false)
|
||||
|
||||
private fun IrExpression.warningIfError(original: IrExpression): IrExpression {
|
||||
if (this is IrErrorExpression) {
|
||||
|
||||
+47
-8
@@ -9,10 +9,15 @@ import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.declarations.JsKlibEsModuleExportsChecker
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.declarations.JsKlibOtherModuleExportsChecker
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.expressions.JsKlibJsCodeCallChecker
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.library.SerializedIrFile
|
||||
|
||||
object JsKlibCheckers {
|
||||
@@ -21,17 +26,51 @@ object JsKlibCheckers {
|
||||
JsKlibOtherModuleExportsChecker
|
||||
)
|
||||
|
||||
private val callCheckers = listOf(
|
||||
JsKlibJsCodeCallChecker
|
||||
)
|
||||
|
||||
fun check(
|
||||
cleanFiles: List<SerializedIrFile>,
|
||||
dirtyFiles: List<IrFile>,
|
||||
dirtyModule: IrModuleFragment,
|
||||
exportedNames: Map<IrFile, Map<IrDeclarationWithName, String>>,
|
||||
diagnosticReporter: DiagnosticReporter,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
val reporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, configuration.languageVersionSettings)
|
||||
val exportedDeclarations = JsKlibExportingDeclaration.collectDeclarations(cleanFiles, dirtyFiles, exportedNames)
|
||||
for (checker in exportedDeclarationsCheckers) {
|
||||
checker.check(exportedDeclarations, reporter)
|
||||
}
|
||||
dirtyModule.acceptVoid(object : IrElementVisitorVoid {
|
||||
private val reporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, configuration.languageVersionSettings)
|
||||
private val diagnosticContext = JsKlibDiagnosticContext(configuration)
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
if (element is IrDeclaration) {
|
||||
diagnosticContext.withDeclarationScope(element) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
} else {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitModuleFragment(declaration: IrModuleFragment) {
|
||||
val exportedDeclarations = JsKlibExportingDeclaration.collectDeclarations(cleanFiles, declaration.files, exportedNames)
|
||||
for (checker in exportedDeclarationsCheckers) {
|
||||
checker.check(exportedDeclarations, this.diagnosticContext, reporter)
|
||||
}
|
||||
super.visitModuleFragment(declaration)
|
||||
}
|
||||
|
||||
override fun visitFile(declaration: IrFile) {
|
||||
diagnosticContext.withFileScope(declaration) {
|
||||
super.visitFile(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall) {
|
||||
for (checker in callCheckers) {
|
||||
checker.check(expression, this.diagnosticContext, reporter)
|
||||
}
|
||||
super.visitCall(expression)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.checkers
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
|
||||
interface JsKlibDeclarationsChecker<D> {
|
||||
fun check(declarations: List<D>, reporter: KtDiagnosticReporterWithImplicitIrBasedContext)
|
||||
fun check(declarations: List<D>, context: JsKlibDiagnosticContext, reporter: KtDiagnosticReporterWithImplicitIrBasedContext)
|
||||
}
|
||||
|
||||
typealias JsKlibExportedDeclarationsChecker = JsKlibDeclarationsChecker<JsKlibExportingDeclaration>
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.ir.backend.js.checkers
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticReporterWithContext
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
|
||||
class JsKlibDiagnosticContext(val compilerConfiguration: CompilerConfiguration) {
|
||||
var containingDeclaration: IrDeclaration? = null
|
||||
private set
|
||||
|
||||
var containingFile: IrFile? = null
|
||||
private set
|
||||
|
||||
fun withDeclarationScope(declaration: IrDeclaration, f: () -> Unit) {
|
||||
val prevDeclaration = containingDeclaration
|
||||
try {
|
||||
containingDeclaration = declaration
|
||||
f()
|
||||
} finally {
|
||||
containingDeclaration = prevDeclaration
|
||||
}
|
||||
}
|
||||
|
||||
fun withFileScope(file: IrFile, f: () -> Unit) {
|
||||
val prevFile = containingFile
|
||||
try {
|
||||
containingFile = file
|
||||
f()
|
||||
} finally {
|
||||
containingFile = prevFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KtDiagnosticReporterWithImplicitIrBasedContext.at(
|
||||
declaration: IrDeclaration,
|
||||
context: JsKlibDiagnosticContext,
|
||||
): KtDiagnosticReporterWithContext.DiagnosticContextImpl {
|
||||
return context.containingFile?.let { at(declaration, it) } ?: at(declaration)
|
||||
}
|
||||
|
||||
fun KtDiagnosticReporterWithImplicitIrBasedContext.at(
|
||||
irElement: IrElement,
|
||||
context: JsKlibDiagnosticContext,
|
||||
): KtDiagnosticReporterWithContext.DiagnosticContextImpl {
|
||||
val file = context.containingFile
|
||||
if (file != null) {
|
||||
return at(irElement, file)
|
||||
}
|
||||
|
||||
val declaration = context.containingDeclaration
|
||||
if (declaration != null) {
|
||||
return at(irElement, declaration)
|
||||
}
|
||||
|
||||
// Should never happen
|
||||
error("Cannot find the expression containing declaration")
|
||||
}
|
||||
+27
@@ -7,14 +7,23 @@ package org.jetbrains.kotlin.ir.backend.js.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.error0
|
||||
import org.jetbrains.kotlin.diagnostics.error1
|
||||
import org.jetbrains.kotlin.diagnostics.error2
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.*
|
||||
import org.jetbrains.kotlin.diagnostics.warning0
|
||||
import org.jetbrains.kotlin.diagnostics.warning1
|
||||
import org.jetbrains.kotlin.diagnostics.warning2
|
||||
|
||||
object JsKlibErrors {
|
||||
val EXPORTING_JS_NAME_CLASH by error2<PsiElement, String, List<JsKlibExport>>()
|
||||
val EXPORTING_JS_NAME_CLASH_ES by warning2<PsiElement, String, List<JsKlibExport>>()
|
||||
|
||||
val JSCODE_CAN_NOT_VERIFY_JAVASCRIPT by warning0<PsiElement>()
|
||||
val JSCODE_NO_JAVASCRIPT_PRODUCED by error0<PsiElement>()
|
||||
val JSCODE_ERROR by error1<PsiElement, String>()
|
||||
val JSCODE_WARNING by warning1<PsiElement, String>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(KtDefaultJsKlibErrorMessages)
|
||||
}
|
||||
@@ -43,5 +52,23 @@ private object KtDefaultJsKlibErrorMessages : BaseDiagnosticRendererFactory() {
|
||||
CommonRenderers.STRING,
|
||||
JS_KLIB_EXPORTS,
|
||||
)
|
||||
map.put(
|
||||
JsKlibErrors.JSCODE_CAN_NOT_VERIFY_JAVASCRIPT,
|
||||
"Cannot verify JavaScript code because the argument is not a constant string"
|
||||
)
|
||||
map.put(
|
||||
JsKlibErrors.JSCODE_NO_JAVASCRIPT_PRODUCED,
|
||||
"An argument for the js() function must be non-empty JavaScript code"
|
||||
)
|
||||
map.put(
|
||||
JsKlibErrors.JSCODE_ERROR,
|
||||
"JavaScript error: {0}",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
map.put(
|
||||
JsKlibErrors.JSCODE_WARNING,
|
||||
"JavaScript warning: {0}",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.ir.backend.js.checkers
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
|
||||
interface JsKlibExpressionChecker<E : IrExpression> {
|
||||
fun check(expression: E, context: JsKlibDiagnosticContext, reporter: KtDiagnosticReporterWithImplicitIrBasedContext)
|
||||
}
|
||||
|
||||
typealias JsKlibCallChecker = JsKlibExpressionChecker<IrCall>
|
||||
+8
-5
@@ -6,14 +6,13 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.checkers.declarations
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibExportingDeclaration
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibExportedDeclarationsChecker
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibErrors
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.*
|
||||
|
||||
object JsKlibEsModuleExportsChecker : JsKlibExportedDeclarationsChecker {
|
||||
override fun check(
|
||||
declarations: List<JsKlibExportingDeclaration>,
|
||||
reporter: KtDiagnosticReporterWithImplicitIrBasedContext,
|
||||
context: JsKlibDiagnosticContext,
|
||||
reporter: KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
) {
|
||||
val allExportedNameClashes = declarations.groupBy { it.exportingName }.filterValues { it.size > 1 }
|
||||
|
||||
@@ -21,7 +20,11 @@ object JsKlibEsModuleExportsChecker : JsKlibExportedDeclarationsChecker {
|
||||
for ((index, exportedDeclaration) in exportedDeclarationClashes.withIndex()) {
|
||||
val declaration = exportedDeclaration.declaration ?: continue
|
||||
val clashedWith = exportedDeclarationClashes.filterIndexed { i, _ -> i != index }
|
||||
reporter.at(declaration).report(JsKlibErrors.EXPORTING_JS_NAME_CLASH_ES, exportedDeclaration.exportingName, clashedWith)
|
||||
reporter.at(declaration, context).report(
|
||||
JsKlibErrors.EXPORTING_JS_NAME_CLASH_ES,
|
||||
exportedDeclaration.exportingName,
|
||||
clashedWith
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -44,11 +44,19 @@ object JsKlibOtherModuleExportsChecker : JsKlibExportedDeclarationsChecker {
|
||||
}
|
||||
}
|
||||
|
||||
override fun check(declarations: List<JsKlibExportingDeclaration>, reporter: KtDiagnosticReporterWithImplicitIrBasedContext) {
|
||||
override fun check(
|
||||
declarations: List<JsKlibExportingDeclaration>,
|
||||
context: JsKlibDiagnosticContext,
|
||||
reporter: KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
) {
|
||||
val clashes = collectClashes(declarations)
|
||||
for ((declaration, clashedWith) in clashes) {
|
||||
if (declaration.declaration != null) {
|
||||
reporter.at(declaration.declaration).report(JsKlibErrors.EXPORTING_JS_NAME_CLASH, declaration.exportingName, clashedWith)
|
||||
reporter.at(declaration.declaration, context).report(
|
||||
JsKlibErrors.EXPORTING_JS_NAME_CLASH,
|
||||
declaration.exportingName,
|
||||
clashedWith
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.ir.backend.js.checkers.expressions
|
||||
|
||||
import com.google.gwt.dev.js.parserExceptions.AbortParsingException
|
||||
import com.google.gwt.dev.js.rhino.CodePosition
|
||||
import com.google.gwt.dev.js.rhino.ErrorReporter
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibCallChecker
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibErrors
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibDiagnosticContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.at
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstKind
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsFunctionScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsProgram
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
|
||||
import org.jetbrains.kotlin.js.parser.parseExpressionOrStatement
|
||||
import org.jetbrains.kotlin.name.JsStandardClassIds
|
||||
|
||||
object JsKlibJsCodeCallChecker : JsKlibCallChecker {
|
||||
private val jsCodeFqName = JsStandardClassIds.Callables.JsCode.asSingleFqName()
|
||||
|
||||
override fun check(expression: IrCall, context: JsKlibDiagnosticContext, reporter: KtDiagnosticReporterWithImplicitIrBasedContext) {
|
||||
// Do not check IR from K1, because there are corresponding K1 FE checks in JsCallChecker
|
||||
if (!context.compilerConfiguration.languageVersionSettings.languageVersion.usesK2) {
|
||||
return
|
||||
}
|
||||
if (expression.symbol.owner.fqNameWhenAvailable != jsCodeFqName) {
|
||||
return
|
||||
}
|
||||
|
||||
val jsCodeExpr = expression.getValueArgument(0)
|
||||
// K2 frontend checker FirJsCodeConstantArgumentChecker checks that the passing argument is a constant.
|
||||
// IrConstOnlyNecessaryTransformer must evaluate an argument expression and fold it into a string constant.
|
||||
if (jsCodeExpr !is IrConst<*> || jsCodeExpr.kind != IrConstKind.String) {
|
||||
// Do not ignore the error if the argument does not fit the expectations.
|
||||
// The IR can be generated in the plugin, avoiding FE checks and IrConstOnlyNecessaryTransformer.
|
||||
// Bugs are also possible in IrConstOnlyNecessaryTransformer.
|
||||
reporter.at(jsCodeExpr ?: expression, context).report(JsKlibErrors.JSCODE_CAN_NOT_VERIFY_JAVASCRIPT)
|
||||
return
|
||||
}
|
||||
|
||||
val jsCodeStr = IrConstKind.String.valueOf(jsCodeExpr)
|
||||
|
||||
try {
|
||||
val parserScope = JsFunctionScope(JsRootScope(JsProgram()), "<js fun>")
|
||||
val fileName = context.containingFile?.fileEntry?.name ?: "<unknown file>"
|
||||
val jsErrorReporter = JsErrorReporter(jsCodeExpr, context, reporter)
|
||||
val statements = parseExpressionOrStatement(jsCodeStr, jsErrorReporter, parserScope, CodePosition(0, 0), fileName)
|
||||
if (statements.isNullOrEmpty()) {
|
||||
reporter.at(jsCodeExpr, context).report(JsKlibErrors.JSCODE_NO_JAVASCRIPT_PRODUCED)
|
||||
}
|
||||
} catch (e: AbortParsingException) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private class JsErrorReporter(
|
||||
val codeExpression: IrExpression,
|
||||
val context: JsKlibDiagnosticContext,
|
||||
val reporter: KtDiagnosticReporterWithImplicitIrBasedContext,
|
||||
) : ErrorReporter {
|
||||
override fun warning(message: String, startPosition: CodePosition, endPosition: CodePosition) {
|
||||
reporter.at(codeExpression, context).report(JsKlibErrors.JSCODE_WARNING, message)
|
||||
}
|
||||
|
||||
override fun error(message: String, startPosition: CodePosition, endPosition: CodePosition) {
|
||||
reporter.at(codeExpression, context).report(JsKlibErrors.JSCODE_ERROR, message)
|
||||
throw AbortParsingException()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -656,7 +656,7 @@ fun serializeModuleIntoKlib(
|
||||
|
||||
if (builtInsPlatform == BuiltInsPlatform.JS) {
|
||||
val cleanFilesIrData = cleanFiles.map { it.irData }
|
||||
JsKlibCheckers.check(cleanFilesIrData, moduleFragment.files, moduleExportedNames, diagnosticReporter, configuration)
|
||||
JsKlibCheckers.check(cleanFilesIrData, moduleFragment, moduleExportedNames, diagnosticReporter, configuration)
|
||||
}
|
||||
|
||||
val serializedIr =
|
||||
|
||||
Reference in New Issue
Block a user