From 678d2e7c213fe9c0a9f9cf398b6ec884f19dd5fa Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 20 Mar 2017 21:10:27 +0300 Subject: [PATCH] KJS: transform labeled blocks to labeled do-while It's a workaround for JDK-8177691. Such code can appear during inline. --- .../LabeledBlockToDoWhileTransformation.kt | 40 ++++++++++++ .../js/test/optimizer/BasicOptimizerTest.kt | 20 +++--- ...LabeledBlockToDoWhileTransformationTest.kt | 29 +++++++++ .../kotlin/js/facade/K2JSTranslator.java | 4 ++ .../simple.optimized.js | 62 +++++++++++++++++++ .../simple.original.js | 62 +++++++++++++++++++ 6 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/LabeledBlockToDoWhileTransformation.kt create mode 100644 js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/LabeledBlockToDoWhileTransformationTest.kt create mode 100644 js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.optimized.js create mode 100644 js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.original.js diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/LabeledBlockToDoWhileTransformation.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/LabeledBlockToDoWhileTransformation.kt new file mode 100644 index 00000000000..9af40ffa8eb --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/LabeledBlockToDoWhileTransformation.kt @@ -0,0 +1,40 @@ +/* + * 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.js.inline.clean + +import org.jetbrains.kotlin.js.backend.ast.* + +object LabeledBlockToDoWhileTransformation { + fun apply(fragments: List) { + for (fragment in fragments) { + apply(fragment.declarationBlock) + apply(fragment.initializerBlock) + } + } + + fun apply(root: JsNode) { + object : JsVisitorWithContextImpl() { + override fun endVisit(x: JsLabel, ctx: JsContext) { + if (x.statement is JsBlock) { + x.statement = JsDoWhile(JsLiteral.FALSE, x.statement) + } + + super.endVisit(x, ctx) + } + }.accept(root) + } +} diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt index 44de9fa6b51..728dd928cd0 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt @@ -17,18 +17,18 @@ package org.jetbrains.kotlin.js.test.optimizer -import org.jetbrains.kotlin.js.backend.ast.metadata.synthetic -import org.jetbrains.kotlin.js.util.TextOutputImpl import com.google.gwt.dev.js.rhino.CodePosition import com.google.gwt.dev.js.rhino.ErrorReporter import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.metadata.synthetic import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor import org.jetbrains.kotlin.js.parser.parse import org.jetbrains.kotlin.js.sourceMap.JsSourceGenerationVisitor import org.jetbrains.kotlin.js.test.BasicBoxTest import org.jetbrains.kotlin.js.test.createScriptEngine import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.util.TextOutputImpl import org.junit.Assert import org.junit.Rule import org.junit.rules.TestName @@ -60,18 +60,22 @@ abstract class BasicOptimizerTest(private var basePath: String) { updateMetadata(unoptimizedCode, unoptimizedAst) for (statement in unoptimizedAst) { - object : RecursiveJsVisitor() { - override fun visitFunction(x: JsFunction) { - FunctionPostProcessor(x).apply() - super.visitFunction(x) - } - }.accept(statement) + process(statement) } val optimizedAst = parse(optimizedCode, errorReporter, parserScope) Assert.assertEquals(astToString(optimizedAst), astToString(unoptimizedAst)) } + protected open fun process(statement: JsStatement) { + object : RecursiveJsVisitor() { + override fun visitFunction(x: JsFunction) { + FunctionPostProcessor(x).apply() + super.visitFunction(x) + } + }.accept(statement) + } + private fun updateMetadata(code: String, ast: List) { val comments = findSyntheticComments(code) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/LabeledBlockToDoWhileTransformationTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/LabeledBlockToDoWhileTransformationTest.kt new file mode 100644 index 00000000000..e72923cbd85 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/LabeledBlockToDoWhileTransformationTest.kt @@ -0,0 +1,29 @@ +/* + * 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.js.test.optimizer + +import org.jetbrains.kotlin.js.backend.ast.JsStatement +import org.jetbrains.kotlin.js.inline.clean.LabeledBlockToDoWhileTransformation +import org.junit.Test + +class LabeledBlockToDoWhileTransformationTest : BasicOptimizerTest("labeled-block-to-do-while") { + @Test fun simple() = box() + + override fun process(statement: JsStatement) { + LabeledBlockToDoWhileTransformation.apply(statement) + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java index bf4930dd848..0a6844db034 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer; import org.jetbrains.kotlin.js.facade.exceptions.TranslationException; import org.jetbrains.kotlin.js.inline.JsInliner; +import org.jetbrains.kotlin.js.inline.clean.LabeledBlockToDoWhileTransformation; import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt; import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt; import org.jetbrains.kotlin.js.translate.general.AstGenerationResult; @@ -124,6 +125,8 @@ public final class K2JSTranslator { JsInliner.process(config, analysisResult.getBindingTrace(), translationResult.getInnerModuleName(), allFragments, newFragments); + LabeledBlockToDoWhileTransformation.INSTANCE.apply(newFragments); + CoroutineTransformer coroutineTransformer = new CoroutineTransformer(translationResult.getProgram()); for (JsProgramFragment fragment : newFragments) { coroutineTransformer.accept(fragment.getDeclarationBlock()); @@ -174,6 +177,7 @@ public final class K2JSTranslator { for (JsImportedModule module : translationResult.getImportedModuleList()) { importedModules.add(module.getExternalName()); } + return new TranslationResult.Success(config, files, translationResult.getProgram(), diagnostics, importedModules, moduleDescriptor, bindingTrace.getBindingContext(), metadataHeader, fileMap); } diff --git a/js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.optimized.js b/js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.optimized.js new file mode 100644 index 00000000000..584040072bf --- /dev/null +++ b/js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.optimized.js @@ -0,0 +1,62 @@ +var a = eval("true"); + +function box() { + var functions = [box1, box2, box3]; + + for (i in functions) { + var f = functions[i]; + var result = f(); + + if (f.toString().indexOf("label: do {") < 0) { + // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8177691 + if (result === "OK") return "Looks like JDK-8177691 fixed for " + f; + if (result !== void 0) return "Result of function changed: " + f; + } + else if (result !== "OK") { + return "fail on " + f + } + } + + return "OK" +} + +function box1() { + label: do { + try { + if (a) throw 1; + } + catch (e) { + break label; + } + throw 2; + } while (false); + + return "OK"; +} + +function box2() { + label: do { + try { + if (a) throw 1; + } + finally { + break label; + } + throw 2; + } while (false); + + return 'OK'; +} + +function box3() { + label: do { + try { + throw 1; + } + finally { + break label; + } + } while (false); + + return 'OK'; +} diff --git a/js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.original.js b/js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.original.js new file mode 100644 index 00000000000..6ac8d9f2c97 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/labeled-block-to-do-while/simple.original.js @@ -0,0 +1,62 @@ +var a = eval("true"); + +function box() { + var functions = [box1, box2, box3]; + + for (i in functions) { + var f = functions[i]; + var result = f(); + + if (f.toString().indexOf("label: do {") < 0) { + // See http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8177691 + if (result === "OK") return "Looks like JDK-8177691 fixed for " + f; + if (result !== void 0) return "Result of function changed: " + f; + } + else if (result !== "OK") { + return "fail on " + f + } + } + + return "OK" +} + +function box1() { + label: { + try { + if (a) throw 1; + } + catch (e) { + break label; + } + throw 2; + } + + return 'OK'; +}; + +function box2() { + label: { + try { + if (a) throw 1; + } + finally { + break label; + } + throw 2; + } + + return 'OK'; +} + +function box3() { + label: { + try { + throw 1; + } + finally { + break label; + } + } + + return 'OK'; +}