KT-12275 Add JS optimization that transforms the following code
```
do {
X
if (B) break;
} while (A)
```
to
```
do {
X
} while (!B && A)
```
Add inversion that takes boolean expression and applies negation to it, simplifying if possible (like !!a => a).
This commit is contained in:
@@ -22,6 +22,7 @@ class FunctionPostProcessor(root: JsFunction) {
|
||||
val optimizations = listOf(
|
||||
{ TemporaryAssignmentElimination(root.body).apply() },
|
||||
{ RedundantLabelRemoval(root.body).apply() },
|
||||
{ WhileConditionFolding(root.body).apply() },
|
||||
{ DoWhileGuardElimination(root.body).apply() },
|
||||
{ TemporaryVariableElimination(root).apply() },
|
||||
{ RedundantBindElimination(root.body).apply() },
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 com.google.dart.compiler.backend.js.ast.*
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
|
||||
class WhileConditionFolding(val body: JsBlock) {
|
||||
private var changed = false
|
||||
|
||||
fun apply(): Boolean {
|
||||
body.accept(object : RecursiveJsVisitor() {
|
||||
override fun visitLabel(x: JsLabel) {
|
||||
val innerStatement = x.statement
|
||||
when (innerStatement) {
|
||||
is JsWhile -> process(innerStatement, x.name)
|
||||
is JsDoWhile -> process(innerStatement, x.name)
|
||||
else -> super.visitLabel(x)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitWhile(x: JsWhile) {
|
||||
process(x, null)
|
||||
}
|
||||
|
||||
override fun visitDoWhile(x: JsDoWhile) {
|
||||
process(x, null)
|
||||
}
|
||||
|
||||
private fun process(statement: JsWhile, name: JsName?) {
|
||||
process(statement, name, { first(it) }, { removeFirst(it) }, { a, b -> JsAstUtils.and(a, b) })
|
||||
}
|
||||
|
||||
private fun process(statement: JsDoWhile, name: JsName?) {
|
||||
if (!hasContinue(statement.body, name)) {
|
||||
process(statement, name, { last(it) }, { removeLast(it) }, { a, b -> JsAstUtils.and(b, a) })
|
||||
}
|
||||
}
|
||||
|
||||
private fun process(
|
||||
statement: JsWhile,
|
||||
name: JsName?,
|
||||
find: (JsStatement) -> JsStatement,
|
||||
remove: (JsStatement) -> JsStatement,
|
||||
combine: (JsExpression, JsExpression) -> JsExpression
|
||||
) {
|
||||
statement.body.accept(this)
|
||||
do {
|
||||
var optimized = false
|
||||
val first = find(statement.body)
|
||||
val condition = extractCondition(first, name)
|
||||
if (condition != null) {
|
||||
statement.body = remove(statement.body)
|
||||
val existingCondition = statement.condition
|
||||
statement.condition = when (existingCondition) {
|
||||
JsLiteral.TRUE -> condition
|
||||
else -> combine(existingCondition, condition)
|
||||
}
|
||||
changed = true
|
||||
optimized = true
|
||||
}
|
||||
} while (optimized)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets condition from while or do..while body and convert it into while/do..while condition
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* while (true) {
|
||||
* if (!A) break;
|
||||
* B()
|
||||
* }
|
||||
*
|
||||
* This function should return `A`, (negated condition of if statement).
|
||||
*/
|
||||
private fun extractCondition(statement: JsStatement, label: JsName?): JsExpression? = when (statement) {
|
||||
// Code like this
|
||||
//
|
||||
// while (A) {
|
||||
// break;
|
||||
// B();
|
||||
// }
|
||||
//
|
||||
// can be rewritten as
|
||||
//
|
||||
// while (A && false) {
|
||||
// B();
|
||||
// }
|
||||
//
|
||||
// therefore for single `break` we should return `false`.
|
||||
is JsBreak -> {
|
||||
val target = statement.label?.name
|
||||
if (label == null || label == target) JsLiteral.FALSE else null
|
||||
}
|
||||
|
||||
// Code like this
|
||||
//
|
||||
// while (A) {
|
||||
// if (!B)
|
||||
// X;
|
||||
// D();
|
||||
// }
|
||||
//
|
||||
// where X is a statement, and we can extract condition `C` from it, can be rewritten as
|
||||
//
|
||||
// while (A && (B || C)) {
|
||||
// D()
|
||||
// }
|
||||
// therefore we return B || C
|
||||
//
|
||||
// an example is
|
||||
//
|
||||
// while (A) {
|
||||
// if (!B)
|
||||
// if (!C)
|
||||
// break;
|
||||
// D()
|
||||
// }
|
||||
//
|
||||
// applying this rule repeatedly we get while (A && (B || C)), which is correct
|
||||
|
||||
is JsIf -> {
|
||||
val then = statement.thenStatement!!
|
||||
if (statement.elseStatement == null) {
|
||||
val nextCondition = extractCondition(then, label)
|
||||
val result: JsExpression? = when (nextCondition) {
|
||||
// Just a little optimization. When inner statement is a single `break`, `nextCondition` would be false.
|
||||
// However, `A || false` can be rewritten as simply `A`
|
||||
JsLiteral.FALSE -> JsAstUtils.negatedOptimized(statement.ifExpression)
|
||||
null -> null
|
||||
else -> JsAstUtils.or(JsAstUtils.negatedOptimized(statement.ifExpression), nextCondition)
|
||||
}
|
||||
result
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// A single block containing a statement with extractable condition
|
||||
is JsBlock -> {
|
||||
if (statement.statements.size == 1) extractCondition(statement.statements[0], label) else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun first(statement: JsStatement) = when (statement) {
|
||||
is JsBlock -> statement.statements.firstOrNull() ?: statement
|
||||
else -> statement
|
||||
}
|
||||
|
||||
private fun removeFirst(statement: JsStatement) = when (statement) {
|
||||
is JsBlock -> {
|
||||
val statements = statement.statements
|
||||
if (statements.isNotEmpty()) {
|
||||
statements.removeAt(0)
|
||||
}
|
||||
statement
|
||||
}
|
||||
else -> JsBlock()
|
||||
}
|
||||
|
||||
private fun last(statement: JsStatement) = when (statement) {
|
||||
is JsBlock -> statement.statements.lastOrNull() ?: statement
|
||||
else -> statement
|
||||
}
|
||||
|
||||
private fun removeLast(statement: JsStatement) = when (statement) {
|
||||
is JsBlock -> {
|
||||
val statements = statement.statements
|
||||
if (statements.isNotEmpty()) {
|
||||
statements.removeAt(statements.lastIndex)
|
||||
}
|
||||
statement
|
||||
}
|
||||
else -> JsBlock()
|
||||
}
|
||||
|
||||
override fun visitFunction(x: JsFunction) { }
|
||||
})
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun hasContinue(statement: JsStatement, label: JsName?): Boolean {
|
||||
var found = false
|
||||
statement.accept(object : RecursiveJsVisitor() {
|
||||
private var level = 0
|
||||
|
||||
override fun visitContinue(x: JsContinue) {
|
||||
val name = x.label?.name
|
||||
if (name == null) {
|
||||
if (level == 0) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
else if (name == label) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFor(x: JsFor) {
|
||||
level++
|
||||
super.visitFor(x)
|
||||
level--
|
||||
}
|
||||
|
||||
override fun visitWhile(x: JsWhile) {
|
||||
level++
|
||||
super.visitWhile(x)
|
||||
level--
|
||||
}
|
||||
|
||||
override fun visitDoWhile(x: JsDoWhile) {
|
||||
level++
|
||||
super.visitDoWhile(x)
|
||||
level--
|
||||
}
|
||||
|
||||
override fun visitForIn(x: JsForIn) {
|
||||
level++
|
||||
super.visitForIn(x)
|
||||
level--
|
||||
}
|
||||
|
||||
override fun visitFunction(x: JsFunction) { }
|
||||
|
||||
override fun visitElement(node: JsNode) {
|
||||
if (!found) {
|
||||
super.visitElement(node)
|
||||
}
|
||||
}
|
||||
})
|
||||
return found
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.junit.Test
|
||||
|
||||
class WhileConditionFoldingTest : BasicOptimizerTest("while-condition-folding") {
|
||||
@Test fun simpleWhile() = box()
|
||||
|
||||
@Test fun simpleDoWhile() = box()
|
||||
|
||||
@Test fun consequentConditions() = box()
|
||||
|
||||
@Test fun nestedConditions() = box()
|
||||
|
||||
@Test fun doWhileWithContinue() = box()
|
||||
|
||||
@Test fun whileEvaluationOrder() = box()
|
||||
|
||||
@Test fun doWhileEvaluationOrder() = box()
|
||||
|
||||
@Test fun inNestedLoop() = box()
|
||||
|
||||
@Test fun inLabeledBlock() = box()
|
||||
|
||||
@Test fun doWhileWithNestedContinue() = box()
|
||||
|
||||
@Test fun labeledContinueInNestedLoop() = box()
|
||||
}
|
||||
-1
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF;
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
|
||||
@@ -238,6 +238,43 @@ public final class JsAstUtils {
|
||||
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression negatedOptimized(@NotNull JsExpression expression) {
|
||||
if (expression instanceof JsUnaryOperation) {
|
||||
JsUnaryOperation unary = (JsUnaryOperation) expression;
|
||||
if (unary.getOperator() == JsUnaryOperator.NOT) return unary.getArg();
|
||||
}
|
||||
else if (expression instanceof JsBinaryOperation) {
|
||||
JsBinaryOperation binary = (JsBinaryOperation) expression;
|
||||
switch (binary.getOperator()) {
|
||||
case AND:
|
||||
return or(negatedOptimized(binary.getArg1()), negatedOptimized(binary.getArg2()));
|
||||
case OR:
|
||||
return and(negatedOptimized(binary.getArg1()), negatedOptimized(binary.getArg2()));
|
||||
case EQ:
|
||||
return new JsBinaryOperation(JsBinaryOperator.NEQ, binary.getArg1(), binary.getArg2());
|
||||
case NEQ:
|
||||
return new JsBinaryOperation(JsBinaryOperator.EQ, binary.getArg1(), binary.getArg2());
|
||||
case REF_EQ:
|
||||
return inequality(binary.getArg1(), binary.getArg2());
|
||||
case REF_NEQ:
|
||||
return equality(binary.getArg1(), binary.getArg2());
|
||||
case LT:
|
||||
return greaterThanEq(binary.getArg1(), binary.getArg2());
|
||||
case LTE:
|
||||
return greaterThan(binary.getArg1(), binary.getArg2());
|
||||
case GT:
|
||||
return lessThanEq(binary.getArg1(), binary.getArg2());
|
||||
case GTE:
|
||||
return lessThan(binary.getArg1(), binary.getArg2());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return negated(expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsBinaryOperation and(@NotNull JsExpression op1, @NotNull JsExpression op2) {
|
||||
return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2);
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (i < 10 && sum <= 30) {
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
|
||||
if (sum != 36) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (true) {
|
||||
if (i >= 10) {
|
||||
break;
|
||||
}
|
||||
if (sum > 30) {
|
||||
break;
|
||||
}
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
|
||||
if (sum != 36) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x + ";";
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
do {
|
||||
sum += i;
|
||||
i++;
|
||||
} while (foo(i) < 10 && foo(sum) <= 30);
|
||||
|
||||
if (global != "2;1;3;3;4;6;5;10;6;15;7;21;8;28;9;36;") return "fail1: " + global;
|
||||
if (sum != 36) return "fail2: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x + ";";
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
do {
|
||||
sum += i;
|
||||
i++;
|
||||
if (foo(i) >= 10) {
|
||||
break;
|
||||
}
|
||||
if (foo(sum) > 30) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != "2;1;3;3;4;6;5;10;6;15;7;21;8;28;9;36;") return "fail1: " + global;
|
||||
if (sum != 36) return "fail2: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
do {
|
||||
++i;
|
||||
if (i == 5) {
|
||||
continue
|
||||
}
|
||||
global += ";";
|
||||
if (foo(i) >= 10) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != ";1;2;3;4;6;7;8;9;10") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
do {
|
||||
++i;
|
||||
if (i == 5) {
|
||||
continue
|
||||
}
|
||||
global += ";";
|
||||
if (foo(i) >= 10) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != ";1;2;3;4;6;7;8;9;10") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
var j;
|
||||
do {
|
||||
++i;
|
||||
global += ";";
|
||||
for (j = 0; j < 2; ++j) {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
global += "-";
|
||||
}
|
||||
for (k in { a: 2, b: 3 }) {
|
||||
if (k != "a") {
|
||||
continue;
|
||||
}
|
||||
global += "@";
|
||||
}
|
||||
j = 0;
|
||||
while (j++ < 2) {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
global += "$";
|
||||
}
|
||||
j = 0;
|
||||
do {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
global += "#";
|
||||
} while (j++ < 2);
|
||||
} while (foo(i) < 3);
|
||||
|
||||
if (global != ";-@$##1;-@$##2;-@$##3") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
js/js.translator/testData/js-optimizer/while-condition-folding/doWhileWithNestedContinue.original.js
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
var j;
|
||||
do {
|
||||
++i;
|
||||
global += ";";
|
||||
for (j = 0; j < 2; ++j) {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
global += "-";
|
||||
}
|
||||
for (k in { a: 2, b: 3 }) {
|
||||
if (k != "a") {
|
||||
continue;
|
||||
}
|
||||
global += "@";
|
||||
}
|
||||
j = 0;
|
||||
while (j++ < 2) {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
global += "$";
|
||||
}
|
||||
j = 0;
|
||||
do {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
global += "#";
|
||||
} while (j++ < 2);
|
||||
if (foo(i) >= 3) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != ";-@$##1;-@$##2;-@$##3") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
outer: {
|
||||
while (i < 10) {
|
||||
sum += i;
|
||||
i++;
|
||||
if (sum > 20) break outer;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum != 21) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
outer: {
|
||||
while (true) {
|
||||
if (i >= 10) {
|
||||
break;
|
||||
}
|
||||
sum += i;
|
||||
i++;
|
||||
if (sum > 20) break outer;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum != 21) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
function box() {
|
||||
var i;
|
||||
var sum = 0;
|
||||
var count = 2;
|
||||
while (count-- > 0) {
|
||||
i = 1;
|
||||
while (i < 10) {
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if (sum != 90) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
function box() {
|
||||
var i;
|
||||
var sum = 0;
|
||||
var count = 2;
|
||||
while (count-- > 0) {
|
||||
i = 1;
|
||||
while (true) {
|
||||
if (i >= 10) {
|
||||
break;
|
||||
}
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if (sum != 90) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
var j;
|
||||
loop: do {
|
||||
++i;
|
||||
global += ";";
|
||||
for (j = 0; j < 2; ++j) {
|
||||
if (j == 1 && i == 2) {
|
||||
continue loop;
|
||||
}
|
||||
global += "-";
|
||||
}
|
||||
if (foo(i) >= 5) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != ";--1;-;--3;--4;--5") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
var j;
|
||||
loop: do {
|
||||
++i;
|
||||
global += ";";
|
||||
for (j = 0; j < 2; ++j) {
|
||||
if (j == 1 && i == 2) {
|
||||
continue loop;
|
||||
}
|
||||
global += "-";
|
||||
}
|
||||
if (foo(i) >= 5) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != ";--1;-;--3;--4;--5") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (i < 5 || sum <= 40) {
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
|
||||
if (sum != 45) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (true) {
|
||||
if (i >= 5) {
|
||||
if (sum > 40) break;
|
||||
}
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
|
||||
if (sum != 45) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
do {
|
||||
++i;
|
||||
global += ";";
|
||||
} while (foo(i) < 10);
|
||||
|
||||
if (global != ";1;2;3;4;5;6;7;8;9;10") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 0;
|
||||
do {
|
||||
++i;
|
||||
global += ";";
|
||||
if (foo(i) >= 10) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (global != ";1;2;3;4;5;6;7;8;9;10") return "fail: " + global;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (i < 10) {
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
|
||||
if (sum != 45) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (true) {
|
||||
if (i >= 10) {
|
||||
break;
|
||||
}
|
||||
sum += i;
|
||||
i++
|
||||
}
|
||||
|
||||
if (sum != 45) return "fail: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x + ";";
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (foo(i) < 10 && foo(sum) <= 30) {
|
||||
sum += i;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (global != "1;0;2;1;3;3;4;6;5;10;6;15;7;21;8;28;9;36;") return "fail1: " + global;
|
||||
if (sum != 36) return "fail2: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
var global = "";
|
||||
|
||||
function foo(x) {
|
||||
global += x + ";";
|
||||
return x;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var i = 1;
|
||||
var sum = 0;
|
||||
while (true) {
|
||||
if (foo(i) >= 10) {
|
||||
break;
|
||||
}
|
||||
if (foo(sum) > 30) {
|
||||
break;
|
||||
}
|
||||
sum += i;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (global != "1;0;2;1;3;3;4;6;5;10;6;15;7;21;8;28;9;36;") return "fail1: " + global;
|
||||
if (sum != 36) return "fail2: " + sum;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user