JS: fix issues with js() usage in inline functions
This commit is contained in:
@@ -26,7 +26,7 @@ public object JsDynamicScope : JsScope(null, "Scope for dynamic declarations", n
|
||||
override fun doCreateName(name: String) = JsName(this, name)
|
||||
}
|
||||
|
||||
public class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description, null) {
|
||||
public open class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description, null) {
|
||||
|
||||
private val labelScopes = Stack<LabelScope>()
|
||||
private val topLabelScope: LabelScope?
|
||||
@@ -36,20 +36,20 @@ public class JsFunctionScope(parent: JsScope, description: String) : JsScope(par
|
||||
|
||||
override fun hasOwnName(name: String): Boolean = RESERVED_WORDS.contains(name) || super.hasOwnName(name)
|
||||
|
||||
public fun declareNameUnsafe(identifier: String): JsName = super.declareName(identifier)
|
||||
public open fun declareNameUnsafe(identifier: String): JsName = super.declareName(identifier)
|
||||
|
||||
public fun enterLabel(label: String): JsName {
|
||||
public open fun enterLabel(label: String): JsName {
|
||||
val scope = LabelScope(topLabelScope, label)
|
||||
labelScopes.push(scope)
|
||||
return scope.labelName
|
||||
}
|
||||
|
||||
public fun exitLabel() {
|
||||
public open fun exitLabel() {
|
||||
assert(labelScopes.isNotEmpty()) { "No scope to exit from" }
|
||||
labelScopes.pop()
|
||||
}
|
||||
|
||||
public fun findLabel(label: String): JsName? =
|
||||
public open fun findLabel(label: String): JsName? =
|
||||
topLabelScope?.findName(label)
|
||||
|
||||
private inner class LabelScope(parent: LabelScope?, val ident: String) : JsScope(parent, "Label scope for $ident", null) {
|
||||
@@ -106,3 +106,36 @@ public class JsFunctionScope(parent: JsScope, description: String) : JsScope(par
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public class DelegatingJsFunctionScopeWithTemporaryParent(
|
||||
private val delegatingScope: JsFunctionScope,
|
||||
parent: JsScope
|
||||
) : JsFunctionScope(parent, "<delegating scope to delegatingScope>") {
|
||||
|
||||
override fun hasOwnName(name: String): Boolean =
|
||||
delegatingScope.hasOwnName(name)
|
||||
|
||||
override fun findOwnName(ident: String): JsName? =
|
||||
delegatingScope.findOwnName(ident)
|
||||
|
||||
override fun declareNameUnsafe(identifier: String): JsName =
|
||||
delegatingScope.declareNameUnsafe(identifier)
|
||||
|
||||
override fun declareName(identifier: String): JsName =
|
||||
delegatingScope.declareName(identifier)
|
||||
|
||||
override fun declareFreshName(suggestedName: String): JsName =
|
||||
delegatingScope.declareFreshName(suggestedName)
|
||||
|
||||
override fun declareTemporary(): JsName =
|
||||
delegatingScope.declareTemporary()
|
||||
|
||||
override fun enterLabel(label: String): JsName =
|
||||
delegatingScope.enterLabel(label)
|
||||
|
||||
override fun exitLabel() =
|
||||
delegatingScope.exitLabel()
|
||||
|
||||
override fun findLabel(label: String): JsName? =
|
||||
delegatingScope.findLabel(label)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunctionScope
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram
|
||||
import com.google.dart.compiler.backend.js.ast.JsRootScope
|
||||
import com.google.gwt.dev.js.parserExceptions.AbortParsingException
|
||||
import com.google.gwt.dev.js.rhino.*
|
||||
import com.google.gwt.dev.js.rhino.Utils.*
|
||||
@@ -85,7 +88,8 @@ public class JsCallChecker : CallChecker {
|
||||
val errorReporter = JsCodeErrorReporter(argument, code, context.trace)
|
||||
|
||||
try {
|
||||
val statements = parse(code, errorReporter, insideFunction = true)
|
||||
val parserScope = JsFunctionScope(JsRootScope(JsProgram("<js checker>")), "<js fun>")
|
||||
val statements = parse(code, errorReporter, parserScope)
|
||||
|
||||
if (statements.size() == 0) {
|
||||
context.trace.report(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED.on(argument))
|
||||
|
||||
@@ -126,7 +126,7 @@ public class FunctionReader(private val context: TranslationContext) {
|
||||
offset++
|
||||
}
|
||||
|
||||
val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter)
|
||||
val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram("<inline>")))
|
||||
val moduleName = getExternalModuleName(descriptor)!!
|
||||
val moduleNameLiteral = context.program().getStringLiteral(moduleName)
|
||||
val moduleReference = context.namer().getModuleReference(moduleNameLiteral)
|
||||
|
||||
@@ -20,11 +20,12 @@ import com.google.dart.compiler.backend.js.ast.*
|
||||
|
||||
import java.util.Stack
|
||||
|
||||
class ScopeContext(private val rootScope: JsScope) {
|
||||
class ScopeContext(scope: JsScope) {
|
||||
private val rootScope = sequence(scope) { it.getParent() }.first { it is JsRootScope }
|
||||
private val scopes = Stack<JsScope>();
|
||||
|
||||
{
|
||||
scopes.push(rootScope)
|
||||
init {
|
||||
scopes.push(scope)
|
||||
}
|
||||
|
||||
fun enterFunction(): JsFunction {
|
||||
|
||||
@@ -27,14 +27,17 @@ import java.util.*
|
||||
|
||||
private val FAKE_SOURCE_INFO = SourceInfoImpl(null, 0, 0, 0, 0)
|
||||
|
||||
public fun parse(code: String, reporter: ErrorReporter, insideFunction: Boolean): List<JsStatement> =
|
||||
parse(code, 0, reporter, insideFunction, Parser::parse).toJsAst(JsAstMapper::mapStatements)
|
||||
public fun parse(code: String, reporter: ErrorReporter, scope: JsScope): List<JsStatement> {
|
||||
val insideFunction = scope is JsFunctionScope
|
||||
val node = parse(code, 0, reporter, insideFunction, Parser::parse)
|
||||
return node.toJsAst(scope, JsAstMapper::mapStatements)
|
||||
}
|
||||
|
||||
public fun parseFunction(code: String, offset: Int, reporter: ErrorReporter): JsFunction =
|
||||
public fun parseFunction(code: String, offset: Int, reporter: ErrorReporter, scope: JsScope): JsFunction =
|
||||
parse(code, offset, reporter, insideFunction = false) {
|
||||
addObserver(FunctionParsingObserver())
|
||||
primaryExpr(it)
|
||||
}.toJsAst(JsAstMapper::mapFunction)
|
||||
}.toJsAst(scope, JsAstMapper::mapFunction)
|
||||
|
||||
private class FunctionParsingObserver : Observer {
|
||||
var functionsStarted = 0
|
||||
@@ -74,15 +77,11 @@ private fun parse(
|
||||
}
|
||||
|
||||
inline
|
||||
private fun Node.toJsAst<T>(mapAction: JsAstMapper.(Node)->T): T =
|
||||
JsAstMapper(RootScope()).mapAction(this)
|
||||
private fun Node.toJsAst<T>(scope: JsScope, mapAction: JsAstMapper.(Node)->T): T =
|
||||
JsAstMapper(scope).mapAction(this)
|
||||
|
||||
private fun StringReader(string: String, offset: Int): Reader {
|
||||
val reader = StringReader(string)
|
||||
reader.skip(offset.toLong())
|
||||
return reader
|
||||
}
|
||||
|
||||
private fun RootScope(): JsScope {
|
||||
return JsRootScope(JsProgram("<parser>"))
|
||||
}
|
||||
@@ -198,6 +198,18 @@ public class InlineJsTestGenerated extends AbstractInlineJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("jsCode.kt")
|
||||
public void testJsCode() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/jsCode.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("jsCodeVarDeclared.kt")
|
||||
public void testJsCodeVarDeclared() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/jsCodeVarDeclared.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaInLambda.kt")
|
||||
public void testLambdaInLambda() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/lambdaInLambda.kt");
|
||||
|
||||
@@ -102,6 +102,12 @@ public class JsCodeTestGenerated extends AbstractJsCodeTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("labelNestedClashWithKotlin.kt")
|
||||
public void testLabelNestedClashWithKotlin() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/jsCode/cases/labelNestedClashWithKotlin.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("labelSiblingClash.kt")
|
||||
public void testLabelSiblingClash() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/jsCode/cases/labelSiblingClash.kt");
|
||||
|
||||
+9
-1
@@ -135,6 +135,14 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
|
||||
assert constant != null: "jsCode must be compile time string " + jsCodeExpression;
|
||||
String jsCode = (String) constant.getValue();
|
||||
assert jsCode != null: jsCodeExpression.toString();
|
||||
return ParserPackage.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE$, /* insideFunction= */ true);
|
||||
|
||||
// Parser can change local or global scope.
|
||||
// In case of js we want to keep new local names,
|
||||
// but no new global ones.
|
||||
JsScope currentScope = context().scope();
|
||||
assert currentScope instanceof JsFunctionScope: "Usage of js outside of function is unexpected";
|
||||
JsScope temporaryRootScope = new JsRootScope(new JsProgram("<js code>"));
|
||||
JsScope scope = new DelegatingJsFunctionScopeWithTemporaryParent((JsFunctionScope) currentScope, temporaryRootScope);
|
||||
return ParserPackage.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE$, scope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: test
|
||||
|
||||
inline fun sum(x: Int, y: Int): Int = js("x + y")
|
||||
|
||||
fun test(x: Int, y: Int): Int = sum(sum(x, x), sum(y, y))
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(4, test(1, 1))
|
||||
assertEquals(8, test(1, 3))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: test
|
||||
|
||||
inline fun sum(x: Int, y: Int): Int = js("var a = x; a + y")
|
||||
|
||||
fun test(x: Int, y: Int): Int {
|
||||
val xx = sum(x, x)
|
||||
js("var a = 0;")
|
||||
val yy = sum(y, y)
|
||||
|
||||
return sum(xx, yy)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(4, test(1, 1))
|
||||
assertEquals(8, test(1, 3))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package foo
|
||||
|
||||
fun box(): String {
|
||||
var sum = 0
|
||||
var sumInner = 0
|
||||
var sumOuter = 0
|
||||
|
||||
val range = 0..10
|
||||
val skipInner = 5
|
||||
val skipOuter = 8
|
||||
|
||||
|
||||
@block for (i in range) {
|
||||
sum += i
|
||||
|
||||
if (i == skipOuter) break@block
|
||||
|
||||
js("""
|
||||
block: {
|
||||
if (i === skipInner) break block;
|
||||
|
||||
sumInner += i
|
||||
}
|
||||
""")
|
||||
|
||||
sumOuter += i
|
||||
}
|
||||
|
||||
assertEquals(sum - skipOuter, sumOuter, "sumOuter")
|
||||
assertEquals(sum - skipOuter - skipInner, sumInner, "sumInner")
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user