[JS IR] Implement MoveTemporaryVariableDeclarationToAssignment optimization
This commit is contained in:
committed by
Space Team
parent
25f7b81d51
commit
524c475834
+1
-1
@@ -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
|
||||
|
||||
|
||||
+202
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+22
@@ -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);
|
||||
|
||||
|
||||
+16
@@ -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")
|
||||
|
||||
+16
@@ -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")
|
||||
|
||||
+16
@@ -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')
|
||||
+16
@@ -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);
|
||||
}
|
||||
Vendored
+105
@@ -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"
|
||||
}
|
||||
Vendored
+112
@@ -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"
|
||||
}
|
||||
js/js.translator/testData/js-optimizer/move-temporary-variable-declaration/notInitUsage.optimized.js
Vendored
+58
@@ -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"
|
||||
}
|
||||
Vendored
+58
@@ -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"
|
||||
}
|
||||
Vendored
+16
@@ -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");
|
||||
}
|
||||
Vendored
+18
@@ -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");
|
||||
}
|
||||
+109
@@ -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"
|
||||
}
|
||||
js/js.translator/testData/js-optimizer/move-temporary-variable-declaration/siblingBlocks.original.js
Vendored
+114
@@ -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
-3
@@ -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";
|
||||
|
||||
+4
-8
@@ -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;
|
||||
|
||||
Vendored
+2
-3
@@ -1,9 +1,8 @@
|
||||
var result = "";
|
||||
|
||||
function box() {
|
||||
var $tmp;
|
||||
$tmp = result;
|
||||
var $tmp = result;
|
||||
result += "fail";
|
||||
result = $tmp + "OK";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user