Adds test infrastructure for JS optimizer. Adds simple tests
This commit is contained in:
@@ -17,6 +17,7 @@ package com.google.gwt.dev.js;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.google.dart.compiler.backend.js.ast.JsLiteral.JsBooleanLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata;
|
||||
import com.google.gwt.dev.js.parserExceptions.JsParserException;
|
||||
import com.google.gwt.dev.js.rhino.*;
|
||||
import com.intellij.util.SmartList;
|
||||
@@ -460,6 +461,12 @@ public class JsAstMapper {
|
||||
|
||||
private JsExpression mapExpression(Node exprNode) throws JsParserException {
|
||||
JsNode unknown = map(exprNode);
|
||||
|
||||
if (unknown instanceof HasMetadata) {
|
||||
HasMetadata metadataContainer = (HasMetadata) unknown;
|
||||
metadataContainer.setData("line", exprNode.getLineno());
|
||||
}
|
||||
|
||||
if (unknown instanceof JsExpression) {
|
||||
return (JsExpression) unknown;
|
||||
}
|
||||
@@ -878,6 +885,12 @@ public class JsAstMapper {
|
||||
|
||||
private JsStatement mapStatement(Node nodeStmt) throws JsParserException {
|
||||
JsNode unknown = map(nodeStmt);
|
||||
|
||||
if (unknown instanceof HasMetadata) {
|
||||
HasMetadata metadataContainer = (HasMetadata) unknown;
|
||||
metadataContainer.setData("line", nodeStmt.getLineno());
|
||||
}
|
||||
|
||||
if (unknown != null) {
|
||||
if (unknown instanceof JsStatement) {
|
||||
return (JsStatement) unknown;
|
||||
|
||||
@@ -328,8 +328,8 @@ public class IRFactory {
|
||||
int lineno)
|
||||
{
|
||||
if (ifFalse == null)
|
||||
return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue);
|
||||
return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, (Node)ifFalse);
|
||||
return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, lineno);
|
||||
return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, (Node)ifFalse, lineno);
|
||||
}
|
||||
|
||||
public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {
|
||||
|
||||
@@ -416,6 +416,7 @@ public class Node implements Cloneable {
|
||||
case TokenStream.BREAK:
|
||||
case TokenStream.CONTINUE:
|
||||
case TokenStream.WITH:
|
||||
case TokenStream.IF:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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 com.google.dart.compiler.backend.js.ast.*
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
||||
import com.google.dart.compiler.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.inline.clean.FunctionPostProcessor
|
||||
import org.jetbrains.kotlin.js.parser.parse
|
||||
import org.jetbrains.kotlin.js.sourceMap.JsSourceGenerationVisitor
|
||||
import org.jetbrains.kotlin.js.test.BasicTest
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.junit.Assert
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TestName
|
||||
import org.mozilla.javascript.Context
|
||||
import java.io.File
|
||||
|
||||
abstract class BasicOptimizerTest(private var basePath: String) {
|
||||
@Rule
|
||||
@JvmField
|
||||
var testName = TestName()
|
||||
|
||||
protected fun box() {
|
||||
val methodName = testName.methodName
|
||||
val baseName = "${BasicTest.TEST_DATA_DIR_PATH}/js-optimizer/$basePath"
|
||||
val unoptimizedName = "$baseName/$methodName.original.js"
|
||||
val optimizedName = "$baseName/$methodName.optimized.js"
|
||||
|
||||
val unoptimizedCode = FileUtil.loadFile(File(unoptimizedName))
|
||||
val optimizedCode = FileUtil.loadFile(File(optimizedName))
|
||||
|
||||
runFiles(unoptimizedName, optimizedName, unoptimizedCode, optimizedCode)
|
||||
checkOptimizer(unoptimizedCode, optimizedCode)
|
||||
}
|
||||
|
||||
private fun runFiles(unoptimizedName: String, optimizedName: String, unoptimizedCode: String, optimizedCode: String) {
|
||||
try {
|
||||
val ctx = Context.enter()
|
||||
runScript(ctx, unoptimizedName, unoptimizedCode)
|
||||
runScript(ctx, optimizedName, optimizedCode)
|
||||
}
|
||||
finally {
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOptimizer(unoptimizedCode: String, optimizedCode: String) {
|
||||
val parserScope = JsFunctionScope(JsRootScope(JsProgram("<js checker>")), "<js fun>")
|
||||
val unoptimizedAst = parse(unoptimizedCode, errorReporter, parserScope)
|
||||
|
||||
updateMetadata(unoptimizedCode, unoptimizedAst)
|
||||
|
||||
for (statement in unoptimizedAst) {
|
||||
object : RecursiveJsVisitor() {
|
||||
override fun visitFunction(x: JsFunction) {
|
||||
FunctionPostProcessor(x.body).apply()
|
||||
super.visitFunction(x)
|
||||
}
|
||||
}.accept(statement)
|
||||
}
|
||||
|
||||
val optimizedAst = parse(optimizedCode, errorReporter, parserScope)
|
||||
Assert.assertEquals(astToString(optimizedAst), astToString(unoptimizedAst))
|
||||
}
|
||||
|
||||
private fun updateMetadata(code: String, ast: List<JsStatement>) {
|
||||
val comments = findSyntheticComments(code)
|
||||
|
||||
for (stmt in ast) {
|
||||
object : RecursiveJsVisitor() {
|
||||
override fun visitVars(x: JsVars) {
|
||||
x.synthetic = x.vars.any { isSyntheticId(it.name.ident) }
|
||||
super.visitVars(x)
|
||||
}
|
||||
|
||||
override fun visitExpressionStatement(x: JsExpressionStatement) {
|
||||
val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression)
|
||||
if (assignment != null) {
|
||||
val name = assignment.first
|
||||
x.synthetic = isSyntheticId(name.ident)
|
||||
}
|
||||
super.visitExpressionStatement(x)
|
||||
}
|
||||
|
||||
override fun visitIf(x: JsIf) {
|
||||
val line = x.getData<Int?>("line")
|
||||
if (line != null && line in comments.indices && comments[line]) {
|
||||
x.synthetic = true
|
||||
}
|
||||
super.visitIf(x)
|
||||
}
|
||||
}.accept(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findSyntheticComments(code: String): List<Boolean> {
|
||||
val parts = code.lines()
|
||||
return parts.map { it.contains("/*synthetic*/") }
|
||||
}
|
||||
|
||||
private fun isSyntheticId(id: String) = id.startsWith("$")
|
||||
|
||||
|
||||
private fun astToString(ast: List<JsStatement>): String {
|
||||
val output = TextOutputImpl()
|
||||
val visitor = JsSourceGenerationVisitor(output, null)
|
||||
for (stmt in ast) {
|
||||
stmt.accept(visitor)
|
||||
}
|
||||
return output.toString()
|
||||
}
|
||||
|
||||
private fun runScript(ctx: Context, fileName: String, code: String) {
|
||||
val scope = ctx.initStandardObjects()
|
||||
ctx.evaluateString(scope, code, fileName, 1, null)
|
||||
val result = ctx.evaluateString(scope, "box()", "unit test", 1, null)
|
||||
|
||||
Assert.assertEquals("box() function must return 'OK'", "OK", result)
|
||||
}
|
||||
|
||||
private val errorReporter = object : ErrorReporter {
|
||||
override fun warning(message: String, startPosition: CodePosition, endPosition: CodePosition) { }
|
||||
|
||||
override fun error(message: String, startPosition: CodePosition, endPosition: CodePosition) {
|
||||
Assert.fail("Error parsing JS file: $message at $startPosition")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 IfStatementReductionTest : BasicOptimizerTest("if-reduction") {
|
||||
@Test fun assignment() = box()
|
||||
|
||||
@Test fun returnStatement() = box()
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 TemporaryAssignmentEliminationTest : BasicOptimizerTest("temporary-assignment") {
|
||||
@Test fun assignment() = box()
|
||||
|
||||
@Test fun returnStatement() = box()
|
||||
|
||||
@Test fun declaration() = box()
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 TemporaryVariableEliminationTest : BasicOptimizerTest("temporary-variable") {
|
||||
@Test fun declaration() = box()
|
||||
|
||||
@Test fun assignment() = box()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
function test(n) {
|
||||
var result;
|
||||
result = n >= 0 ? n : -n;
|
||||
return result;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
function test(n) {
|
||||
var result;
|
||||
/*synthetic*/ if (n >= 0) {
|
||||
result = n;
|
||||
}
|
||||
else {
|
||||
result = -n;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
function test(n) {
|
||||
return n >= 0 ? n : -n;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
function test(n) {
|
||||
/*synthetic*/ if (n >= 0) {
|
||||
return n;
|
||||
}
|
||||
else {
|
||||
return -n;
|
||||
}
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
function test(n) {
|
||||
var result;
|
||||
if (n >= 0) {
|
||||
result = n;
|
||||
}
|
||||
else {
|
||||
result = -n;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
function test(n) {
|
||||
var result;
|
||||
var $tmp;
|
||||
if (n >= 0) {
|
||||
$tmp = n;
|
||||
}
|
||||
else {
|
||||
$tmp = -n;
|
||||
}
|
||||
|
||||
result = $tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
function test(n) {
|
||||
var result;
|
||||
if (n >= 0) {
|
||||
result = n;
|
||||
}
|
||||
else {
|
||||
result = -n;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20)
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20)
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
function test(n) {
|
||||
var $tmp;
|
||||
if (n >= 0) {
|
||||
$tmp = n;
|
||||
}
|
||||
else {
|
||||
$tmp = -n;
|
||||
}
|
||||
var result = $tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
function test(n) {
|
||||
if (n >= 0) {
|
||||
return n;
|
||||
}
|
||||
else {
|
||||
return -n;
|
||||
}
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
function test(n) {
|
||||
var $tmp;
|
||||
if (n >= 0) {
|
||||
$tmp = n;
|
||||
}
|
||||
else {
|
||||
$tmp = -n;
|
||||
}
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(20);
|
||||
if (result != 20) return "fail1: " + result;
|
||||
|
||||
result = test(-20);
|
||||
if (result != 20) return "fail2: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
function test(a, b, c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(2, 3, 4);
|
||||
if (result != 9) return "fail: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
function test(a, b, c) {
|
||||
var $tmp;
|
||||
$tmp = a + b;
|
||||
return $tmp + c;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(2, 3, 4);
|
||||
if (result != 9) return "fail: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
function test(a, b, c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(2, 3, 4);
|
||||
if (result != 9) return "fail: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
function test(a, b, c) {
|
||||
var $tmp = a + b;
|
||||
return $tmp + c;
|
||||
}
|
||||
|
||||
function box() {
|
||||
var result = test(2, 3, 4);
|
||||
if (result != 9) return "fail: " + result;
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user