[JS IR] Implement MoveTemporaryVariableDeclarationToAssignment optimization

This commit is contained in:
Alexander Korepanov
2023-07-06 17:24:28 +02:00
committed by Space Team
parent 25f7b81d51
commit 524c475834
23 changed files with 929 additions and 25 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ fun baz(v:(() -> Unit)) {
// test.kt:8 box
// EXPECTATIONS JS_IR
// test1.kt:12 box
// test1.kt:11 box
// test.kt:5 box
// test3.kt:15 baz
// test1.kt:9 box$lambda
@@ -31,7 +31,8 @@ class FunctionPostProcessor(val root: JsFunction) {
{ RedundantVariableDeclarationElimination(root.body).apply() },
{ RedundantStatementElimination(root).apply() },
{ CoroutineStateElimination(root.body).apply() },
{ BoxingUnboxingElimination(root.body).apply() }
{ BoxingUnboxingElimination(root.body).apply() },
{ MoveTemporaryVariableDeclarationToAssignment(root.body).apply() }
)
// TODO: reduce to A || B, A && B if possible
@@ -0,0 +1,202 @@
/*
* 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.js.inline.clean
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.synthetic
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import java.util.LinkedHashSet
/**
* Moving a declaration of the temporary variable without an initializer to the closest assignment.
*
* Basic example:
* var $tmp;
* $tmp = <expr>;
* Transformed to:
* var $tmp = <expr>;
*
* The optimization algorithm does the following:
* 1) Collects all temporary variables whose declarations do not have an initializer.
* 2) Collects the JsBlock set of all blocks where the temporary variables are assigned and used.
* 3) Ignores the temporary variable if it is used between the declaration and the first assignment.
* 4) Represents the collected JsBlocks as a tree.
* 5) Searches for the Lowest Common Ancestor (LCA) among all JsBlocks where the temporary variables are assigned or used.
* 6) If the LCA is found in the set of JsBlocks where the temporary variable is assigned, we can move the declaration to the assignment.
*/
class MoveTemporaryVariableDeclarationToAssignment(private val body: JsBlock) {
private var hasChanges = false
private val varUsedInBlocks = hashMapOf<JsName, HashSet<JsBlock>>()
private val varAssignedInBlocks = hashMapOf<JsName, HashSet<JsBlock>>()
private val blockParents = hashMapOf<JsBlock, JsBlock>()
private val varWithoutInitDeclarations = hashSetOf<JsName>()
private val varBeforeAssignmentUsages = hashSetOf<JsName>()
private val removedVarDeclarations = hashSetOf<JsName>()
fun apply(): Boolean {
analyze()
perform()
require(removedVarDeclarations.isEmpty())
return hasChanges
}
private fun analyze() {
val visitor = object : RecursiveJsVisitor() {
private val blockStack = mutableListOf<JsBlock>()
private val currentBlock
get() = blockStack.last()
override fun visitBlock(x: JsBlock) {
if (blockStack.isNotEmpty()) {
blockParents[x] = currentBlock
}
blockStack += x
super.visitBlock(x)
blockStack.removeLast()
}
override fun visit(x: JsVars.JsVar) {
if (x.initExpression == null && x.synthetic) {
varWithoutInitDeclarations += x.name
}
super.visit(x)
}
override fun visitVars(x: JsVars) {
if (x.synthetic) {
for (variable in x) {
if (variable.initExpression == null) {
varWithoutInitDeclarations += variable.name
}
}
}
super.visitVars(x)
}
override fun visitNameRef(nameRef: JsNameRef) {
val name = nameRef.name
if (name != null && name in varWithoutInitDeclarations) {
varUsedInBlocks.getOrPut(name) { hashSetOf() } += currentBlock
val assignments = varAssignedInBlocks[name] ?: emptySet()
val hasAssignment = blockStack.any { it in assignments }
if (!hasAssignment) {
varBeforeAssignmentUsages += name
}
}
super.visitNameRef(nameRef)
}
override fun visitExpressionStatement(x: JsExpressionStatement) {
val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression)
if (assignment != null) {
val (name, _) = assignment
if (name in varWithoutInitDeclarations) {
varAssignedInBlocks.getOrPut(name) { hashSetOf() } += currentBlock
}
}
super.visitExpressionStatement(x)
}
}
visitor.accept(body)
}
private fun removeNonParentBlocks(block: JsBlock, parents: LinkedHashSet<JsBlock>) {
var blockParent: JsBlock? = block
while (blockParent != null) {
if (blockParent !in parents) {
blockParent = blockParents[blockParent]
continue
}
val parentIter = parents.iterator()
while (parentIter.hasNext()) {
if (parentIter.next() != blockParent) {
parentIter.remove()
} else {
return
}
}
}
}
private fun calculateLCA(blocks: Set<JsBlock>): JsBlock {
require(blocks.isNotEmpty())
val parents = LinkedHashSet<JsBlock>()
var firstBlockParent: JsBlock? = blocks.first()
while (firstBlockParent != null) {
parents += firstBlockParent
firstBlockParent = blockParents[firstBlockParent]
}
for (block in blocks.asSequence().drop(1)) {
removeNonParentBlocks(block, parents)
}
require(parents.isNotEmpty())
return parents.first()
}
private fun perform() {
val visitor = object : JsVisitorWithContextImpl() {
private fun canRemoveDeclarationWithoutInit(name: JsName): Boolean {
if (name !in varWithoutInitDeclarations || name in varBeforeAssignmentUsages) {
return false
}
val assignedInBlocks = varAssignedInBlocks[name] ?: return false
val usedInBlocks = varUsedInBlocks[name] ?: emptySet()
val lcaBlock = calculateLCA(usedInBlocks + assignedInBlocks)
return lcaBlock in assignedInBlocks
}
override fun endVisit(x: JsVars.JsVar, ctx: JsContext<*>) {
if (canRemoveDeclarationWithoutInit(x.name)) {
removedVarDeclarations += x.name
ctx.removeMe()
hasChanges = true
} else {
super.endVisit(x, ctx)
}
}
override fun endVisit(x: JsVars, ctx: JsContext<*>) {
if (x.isEmpty) {
ctx.removeMe()
hasChanges = true
} else {
super.endVisit(x, ctx)
}
}
override fun endVisit(x: JsExpressionStatement, ctx: JsContext<JsNode>) {
val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression)
if (assignment != null) {
val (name, initExpr) = assignment
if (removedVarDeclarations.remove(name)) {
val varDeclarationWithInit = JsVars.JsVar(name, initExpr).apply { synthetic = true }
val vars = JsVars(varDeclarationWithInit).apply { synthetic = true }
ctx.replaceMe(vars)
}
accept(initExpr)
} else {
super.endVisit(x, ctx)
}
}
}
visitor.accept(body)
}
}
@@ -17,12 +17,13 @@ import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
import org.jetbrains.kotlin.test.services.isKtFile
import java.io.File
class JsAstHandler(testServices: TestServices) : JsBinaryArtifactHandler(testServices) {
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
override fun processModule(module: TestModule, info: BinaryArtifacts.Js) {
val ktFiles = module.files.filter { it.isKtFile }.map { it.originalContent }
val ktFiles = module.files.filter { it.isKtFile }.associate { it.originalFile to it.originalContent }
val jsProgram = when (val artifact = info.unwrap()) {
is BinaryArtifacts.Js.OldJsArtifact -> (artifact.translationResult as TranslationResult.Success).program
is BinaryArtifacts.Js.JsIrArtifact -> artifact.compilerResult.outputs[TranslationMode.FULL_DEV]?.jsProgram ?: return
@@ -31,8 +32,8 @@ class JsAstHandler(testServices: TestServices) : JsBinaryArtifactHandler(testSer
processJsProgram(jsProgram, ktFiles, module.targetBackend!!)
}
private fun processJsProgram(program: JsProgram, psiFiles: List<String>, targetBackend: TargetBackend) {
psiFiles.forEach { DirectiveTestUtils.processDirectives(program, it, targetBackend) }
private fun processJsProgram(program: JsProgram, psiFiles: Map<File, String>, targetBackend: TargetBackend) {
psiFiles.forEach { DirectiveTestUtils.processDirectives(program, it.key, it.value, targetBackend) }
program.verifyAst(targetBackend)
}
@@ -0,0 +1,22 @@
/*
* 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.js.testOld.optimizer
import org.junit.Test
class MoveTemporaryVariableDeclarationToAssignmentTest : BasicOptimizerTest("move-temporary-variable-declaration") {
@Test
fun sameBlock() = box()
@Test
fun innerBlock() = box()
@Test
fun siblingBlocks() = box()
@Test
fun notInitUsage() = box()
}
@@ -23,9 +23,11 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.inline.util.CollectUtilsKt;
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.junit.runners.model.MultipleFailureException;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@@ -97,6 +99,18 @@ public class DirectiveTestUtils {
}
};
private static final DirectiveHandler EXPECT_GENERATED_JS = new DirectiveHandler("EXPECT_GENERATED_JS") {
@Override
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
String functionName = arguments.getNamedArgument("function");
String expected = arguments.getNamedArgument("expect");
File expectedFile = new File(arguments.sourceFile.getParentFile(), expected);
String code = AstSearchUtil.getFunction(ast, functionName).toString();
String msg = "Function '" + functionName + "' got different generated JS code";
KotlinTestUtils.assertEqualsToFile(msg, expectedFile, code);
}
};
private static final DirectiveHandler FUNCTION_EXISTS = new DirectiveHandler("CHECK_FUNCTION_EXISTS") {
@Override
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
@@ -481,6 +495,7 @@ public class DirectiveTestUtils {
};
private static final List<DirectiveHandler> DIRECTIVE_HANDLERS = Arrays.asList(
EXPECT_GENERATED_JS,
FUNCTION_CONTAINS_NO_CALLS,
FUNCTION_NOT_CALLED,
FUNCTION_CALLED_TIMES,
@@ -515,12 +530,13 @@ public class DirectiveTestUtils {
public static void processDirectives(
@NotNull JsNode ast,
@NotNull File sourceFile,
@NotNull String sourceCode,
@NotNull TargetBackend targetBackend
) throws Exception {
List<Throwable> assertionErrors = new ArrayList<>();
for (DirectiveHandler handler : DIRECTIVE_HANDLERS) {
handler.process(ast, sourceCode, targetBackend, assertionErrors);
handler.process(ast, sourceFile, sourceCode, targetBackend, assertionErrors);
}
MultipleFailureException.assertEmpty(assertionErrors);
}
@@ -651,13 +667,15 @@ public class DirectiveTestUtils {
*
* @see ArgumentsHelper for arguments format
*/
void process(@NotNull JsNode ast, @NotNull String sourceCode,
void process(@NotNull JsNode ast,
@NotNull File sourceFile,
@NotNull String sourceCode,
@NotNull TargetBackend targetBackend,
List<Throwable> assertionErrors
) throws Exception {
List<String> directiveEntries = findLinesWithPrefixesRemoved(sourceCode, directive);
for (String directiveEntry : directiveEntries) {
ArgumentsHelper arguments = new ArgumentsHelper(directiveEntry);
ArgumentsHelper arguments = new ArgumentsHelper(directiveEntry, sourceFile);
if (!containsBackend(targetBackend, TARGET_BACKENDS, arguments, true) ||
containsBackend(targetBackend, IGNORED_BACKENDS, arguments, false)) {
continue;
@@ -696,9 +714,10 @@ public class DirectiveTestUtils {
private final Map<String, String> namedArguments = new HashMap<>();
private final String entry;
private final Pattern argumentsPattern = Pattern.compile("[\\w$_;\\.]+(=((\".*?\")|[\\w$_;\\.]+))?");
ArgumentsHelper(@NotNull String directiveEntry) {
final File sourceFile;
ArgumentsHelper(@NotNull String directiveEntry, @NotNull File directiveSourceFile) {
entry = directiveEntry;
sourceFile = directiveSourceFile;
Matcher matcher = argumentsPattern.matcher(directiveEntry);
@@ -6919,6 +6919,22 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/jsAstOptimizations")
@TestDataPath("$PROJECT_ROOT")
public class JsAstOptimizations {
@Test
public void testAllFilesPresentInJsAstOptimizations() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/jsAstOptimizations"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("tempVarDeclOnAssignment.kt")
public void testTempVarDeclOnAssignment() throws Exception {
runTest("js/js.translator/testData/box/jsAstOptimizations/tempVarDeclOnAssignment.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/jsCode")
@TestDataPath("$PROJECT_ROOT")
@@ -7025,6 +7025,22 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/jsAstOptimizations")
@TestDataPath("$PROJECT_ROOT")
public class JsAstOptimizations {
@Test
public void testAllFilesPresentInJsAstOptimizations() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/jsAstOptimizations"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@Test
@TestMetadata("tempVarDeclOnAssignment.kt")
public void testTempVarDeclOnAssignment() throws Exception {
runTest("js/js.translator/testData/box/jsAstOptimizations/tempVarDeclOnAssignment.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/jsCode")
@TestDataPath("$PROJECT_ROOT")
@@ -6919,6 +6919,22 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/jsAstOptimizations")
@TestDataPath("$PROJECT_ROOT")
public class JsAstOptimizations {
@Test
public void testAllFilesPresentInJsAstOptimizations() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/jsAstOptimizations"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("tempVarDeclOnAssignment.kt")
public void testTempVarDeclOnAssignment() throws Exception {
runTest("js/js.translator/testData/box/jsAstOptimizations/tempVarDeclOnAssignment.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/jsCode")
@TestDataPath("$PROJECT_ROOT")
@@ -0,0 +1,10 @@
inline fun jsUpper(c: Char) = c.toString().asDynamic().toUpperCase().unsafeCast<String>()
inline fun upper(c: Char): Char = jsUpper(c)[0]
// EXPECT_GENERATED_JS: function=test expect=tempVarDeclOnAssignmentTest.js
fun test(a: Char, b: Char): String {
return "${upper(a)}${upper(b)}"
}
fun box() = test('o', 'k')
@@ -0,0 +1,16 @@
function test(a, b) {
// Inline function 'upper' call
// Inline function 'jsUpper' call
// Inline function 'kotlin.js.unsafeCast' call
// Inline function 'kotlin.js.asDynamic' call
var tmp$ret$2 = toString(a).toUpperCase();
var tmp$ret$3 = charSequenceGet(tmp$ret$2, 0);
var tmp = toString(tmp$ret$3);
// Inline function 'upper' call
// Inline function 'jsUpper' call
// Inline function 'kotlin.js.unsafeCast' call
// Inline function 'kotlin.js.asDynamic' call
var tmp$ret$6 = toString(b).toUpperCase();
var tmp$ret$7 = charSequenceGet(tmp$ret$6, 0);
return tmp + toString(tmp$ret$7);
}
@@ -0,0 +1,105 @@
function getArg(x) {
return x;
}
function test1(o, k) {
if (o != k) {
var $tmp1 = getArg(k).toUpperCase();
var $tmp2 = getArg(o).toUpperCase();
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
}
function test2(o, k) {
if (o != k) {
var $tmp1 = getArg(k).toUpperCase();
var $tmp2 = getArg(o).toUpperCase();
if (o) {
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
}
}
function test3(o, k) {
if (o != k) {
var $tmp1 = getArg(k).toUpperCase();
if (o) {
var $tmp2 = getArg(o).toUpperCase();
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
}
}
function test4(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
}
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
function test5(ok) {
var $tmp;
if (ok) {
if (ok) {
if (ok) {
if (ok) {
$tmp = getArg(ok).toUpperCase();
}
}
var OK = getArg($tmp);
}
}
return OK
}
function test6(ok) {
if (ok) {
if (ok) {
var $tmp = 1
if (ok) {
if (ok) {
$tmp = getArg(ok).toUpperCase();
}
var OK = getArg($tmp);
}
}
}
return OK
}
function box() {
if (test1("o", "k") != "OK") {
return "Fail test1"
}
if (test2("o", "k") != "OK") {
return "Fail test2"
}
if (test3("o", "k") != "OK") {
return "Fail test3"
}
if (test4("o", "k") != "OK") {
return "Fail test4"
}
if (test5("ok") != "OK") {
return "Fail test5"
}
if (test6("ok") != "OK") {
return "Fail test6"
}
return "OK"
}
@@ -0,0 +1,112 @@
function getArg(x) {
return x;
}
function test1(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
}
function test2(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
if (o) {
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
}
}
function test3(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
if (o) {
$tmp2 = getArg(o).toUpperCase();
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
}
}
function test4(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
}
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
function test5(ok) {
var $tmp;
if (ok) {
if (ok) {
if (ok) {
if (ok) {
$tmp = getArg(ok).toUpperCase();
}
}
var OK = getArg($tmp);
}
}
return OK
}
function test6(ok) {
var $tmp;
if (ok) {
if (ok) {
$tmp = 1
if (ok) {
if (ok) {
$tmp = getArg(ok).toUpperCase();
}
var OK = getArg($tmp);
}
}
}
return OK
}
function box() {
if (test1("o", "k") != "OK") {
return "Fail test1"
}
if (test2("o", "k") != "OK") {
return "Fail test2"
}
if (test3("o", "k") != "OK") {
return "Fail test3"
}
if (test4("o", "k") != "OK") {
return "Fail test4"
}
if (test5("ok") != "OK") {
return "Fail test5"
}
if (test6("ok") != "OK") {
return "Fail test6"
}
return "OK"
}
@@ -0,0 +1,58 @@
function getArg(x) {
return x;
}
function test1(ok) {
var $tmp
getArg($tmp)
$tmp = getArg(ok).toUpperCase();
return $tmp
}
function test2(ok) {
var $tmp
if ($tmp === undefined) {
$tmp = getArg(ok).toUpperCase();
}
return $tmp
}
function test3(ok) {
var $tmp
if (ok) {
if (ok) {
getArg($tmp)
$tmp = getArg(ok).toUpperCase();
}
}
return $tmp
}
function test4(ok) {
var $tmp
if (ok) {
if (ok) {
getArg($tmp)
}
$tmp = getArg(ok).toUpperCase();
}
return $tmp
}
function box() {
if (test1("ok") != "OK") {
return "Fail test1"
}
if (test2("ok") != "OK") {
return "Fail test2"
}
if (test3("ok") != "OK") {
return "Fail test3"
}
if (test4("ok") != "OK") {
return "Fail test4"
}
return "OK"
}
@@ -0,0 +1,58 @@
function getArg(x) {
return x;
}
function test1(ok) {
var $tmp
getArg($tmp)
$tmp = getArg(ok).toUpperCase();
return $tmp
}
function test2(ok) {
var $tmp
if ($tmp === undefined) {
$tmp = getArg(ok).toUpperCase();
}
return $tmp
}
function test3(ok) {
var $tmp
if (ok) {
if (ok) {
getArg($tmp)
$tmp = getArg(ok).toUpperCase();
}
}
return $tmp
}
function test4(ok) {
var $tmp
if (ok) {
if (ok) {
getArg($tmp)
}
$tmp = getArg(ok).toUpperCase();
}
return $tmp
}
function box() {
if (test1("ok") != "OK") {
return "Fail test1"
}
if (test2("ok") != "OK") {
return "Fail test2"
}
if (test3("ok") != "OK") {
return "Fail test3"
}
if (test4("ok") != "OK") {
return "Fail test4"
}
return "OK"
}
@@ -0,0 +1,16 @@
function getArg(x) {
return x;
}
function test(o, k) {
var $tmp1 = getArg(k).toUpperCase();
var $tmp2 = getArg(o).toUpperCase();
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
function box() {
return test("o", "k");
}
@@ -0,0 +1,18 @@
function getArg(x) {
return x;
}
function test(o, k) {
var $tmp1;
var $tmp2;
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
function box() {
return test("o", "k");
}
@@ -0,0 +1,109 @@
function getArg(x) {
return x;
}
function test1(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
} else {
$tmp1 = getArg(k)
$tmp2 = getArg(o)
}
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
function test2(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
if (o) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
} else {
$tmp1 = getArg(k)
$tmp2 = getArg(o)
}
var O = getArg($tmp2);
var K = getArg($tmp1);
}
return O + K;
}
function test3(o, k) {
if (o != k) {
var $tmp1 = getArg(k).toUpperCase();
var $tmp2 = getArg(k).toUpperCase();
if (o) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
} else {
$tmp1 = getArg(k)
$tmp2 = getArg(o)
}
var O = getArg($tmp2);
var K = getArg($tmp1);
}
return O + K;
}
function test4(ok) {
if (ok) {
var $tmp1 = getArg(ok).toUpperCase();
if (ok) {
if (ok) {
getArg($tmp1)
}
} else {
getArg($tmp1)
}
var OK = getArg($tmp1);
}
return OK;
}
function test5(ok) {
if (ok) {
var $tmp1 = 0
if (ok) {
$tmp1 = 1
if (ok) {
getArg($tmp1)
} else {
$tmp1 = 2
}
$tmp1 = getArg(ok).toUpperCase();
} else {
getArg($tmp1)
}
var OK = getArg($tmp1);
}
return OK;
}
function box() {
if (test1("o", "k") != "OK") {
return "Fail test1"
}
if (test2("o", "k") != "OK") {
return "Fail test2"
}
if (test3("o", "k") != "OK") {
return "Fail test3"
}
if (test4("OK") != "OK") {
return "Fail test4"
}
if (test5("OK") != "OK") {
return "Fail test5"
}
return "OK"
}
@@ -0,0 +1,114 @@
function getArg(x) {
return x;
}
function test1(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
} else {
$tmp1 = getArg(k)
$tmp2 = getArg(o)
}
var O = getArg($tmp2);
var K = getArg($tmp1);
return O + K;
}
function test2(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
if (o) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
} else {
$tmp1 = getArg(k)
$tmp2 = getArg(o)
}
var O = getArg($tmp2);
var K = getArg($tmp1);
}
return O + K;
}
function test3(o, k) {
var $tmp1;
var $tmp2;
if (o != k) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(k).toUpperCase();
if (o) {
$tmp1 = getArg(k).toUpperCase();
$tmp2 = getArg(o).toUpperCase();
} else {
$tmp1 = getArg(k)
$tmp2 = getArg(o)
}
var O = getArg($tmp2);
var K = getArg($tmp1);
}
return O + K;
}
function test4(ok) {
var $tmp1;
if (ok) {
$tmp1 = getArg(ok).toUpperCase();
if (ok) {
if (ok) {
getArg($tmp1)
}
} else {
getArg($tmp1)
}
var OK = getArg($tmp1);
}
return OK;
}
function test5(ok) {
var $tmp1;
if (ok) {
$tmp1 = 0
if (ok) {
$tmp1 = 1
if (ok) {
getArg($tmp1)
} else {
$tmp1 = 2
}
$tmp1 = getArg(ok).toUpperCase();
} else {
getArg($tmp1)
}
var OK = getArg($tmp1);
}
return OK;
}
function box() {
if (test1("o", "k") != "OK") {
return "Fail test1"
}
if (test2("o", "k") != "OK") {
return "Fail test2"
}
if (test3("o", "k") != "OK") {
return "Fail test3"
}
if (test4("OK") != "OK") {
return "Fail test4"
}
if (test5("OK") != "OK") {
return "Fail test5"
}
return "OK"
}
@@ -3,18 +3,18 @@ function f(x) {
}
function box() {
var result1, result2, $a, $b;
var $a, $b;
if (f(true)) {
$a = "1";
}
result1 = $a;
var result1 = $a;
if (result1 !== "1") return "fail1: " + result1;
if (f(false)) {
$b = "1";
}
result2 = $b;
var result2 = $b;
if (result2 !== void 0) return "fail2: " + result2;
return "OK";
@@ -13,32 +13,28 @@ function testIrregular() {
}
function testDoubleUse1() {
var $b;
$b = f("testDoubleUse1");
var $b = f("testDoubleUse1");
var result = $b;
f($b);
return result;
}
function testDoubleUse2() {
var $d;
$d = f("testDoubleUse2");
var $d = f("testDoubleUse2");
var result = $d;
f($d);
return result;
}
function testDoubleUse3() {
var $a;
$a = f("testDoubleUse3");
var $a = f("testDoubleUse3");
var result = $a;
f($a);
return result;
}
function testCircular() {
var $b;
$b = f("testCircular");
var $b = f("testCircular");
$b = $b;
var result = $b;
return result;
@@ -1,9 +1,8 @@
var result = "";
function box() {
var $tmp;
$tmp = result;
var $tmp = result;
result += "fail";
result = $tmp + "OK";
return result;
}
}
+1 -1
View File
@@ -15,5 +15,5 @@ suspend fun bar(): Unit {
}
// LINES(JS): 39 4 4 4 7 5 5 45 45 5 93 45 5 5 6 4 4 4 9 15 9 9 9 * 9 15 10 10 11 11 11 11 11 * 11 12 12 13 13 13 13 13 13 13 14 14 * 9 15 9 9 9 9
// LINES(JS_IR): 4 4 * 93 93 3 45 45 7 7 6 9 9 * 9 * 9 * 10 10 * 11 * 11 12 12 * 13 * 13 14 14 15 15
// LINES(JS_IR): 4 4 * 93 3 45 45 7 7 6 9 9 * 9 * 9 * 10 10 * 11 * 11 12 12 * 13 * 13 14 14 15 15