JsTests speed up: cash the scope with Kotlin library. Refactor CodeGenerator class.

About 10% speed up (not much but still...)
This commit is contained in:
Pavel V. Talanov
2012-06-29 13:24:24 +04:00
parent 72271cbdd4
commit 49c6555e5e
7 changed files with 117 additions and 69 deletions
@@ -42,9 +42,6 @@ public abstract class BasicTest extends TestWithEnvironment {
private static final String TEST_FILES = "js/js.translator/testFiles/"; private static final String TEST_FILES = "js/js.translator/testFiles/";
private static final String CASES = "cases/"; private static final String CASES = "cases/";
private static final String OUT = "out/"; private static final String OUT = "out/";
private static final String KOTLIN_JS_LIB = pathToTestFilesRoot() + "kotlin_lib.js";
private static final String KOTLIN_JS_LIB_ECMA_3 = pathToTestFilesRoot() + "kotlin_lib_ecma3.js";
private static final String KOTLIN_JS_LIB_ECMA_5 = pathToTestFilesRoot() + "kotlin_lib_ecma5.js";
private static final String EXPECTED = "expected/"; private static final String EXPECTED = "expected/";
@NotNull @NotNull
@@ -90,10 +87,7 @@ public abstract class BasicTest extends TestWithEnvironment {
@NotNull @NotNull
protected List<String> additionalJSFiles(@NotNull EcmaVersion ecmaVersion) { protected List<String> additionalJSFiles(@NotNull EcmaVersion ecmaVersion) {
List<String> list = Lists.newArrayList(); return Lists.newArrayList();
list.add(ecmaVersion == EcmaVersion.v5 ? KOTLIN_JS_LIB_ECMA_5 : KOTLIN_JS_LIB_ECMA_3);
list.add(KOTLIN_JS_LIB);
return list;
} }
protected void generateJavaScriptFiles(@NotNull String kotlinFilename, protected void generateJavaScriptFiles(@NotNull String kotlinFilename,
@@ -141,7 +135,7 @@ public abstract class BasicTest extends TestWithEnvironment {
} }
@NotNull @NotNull
protected static String pathToTestFilesRoot() { public static String pathToTestFilesRoot() {
return TEST_FILES; return TEST_FILES;
} }
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
import static org.jetbrains.k2js.test.rhino.RhinoUtils.flushSystemOut;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
@@ -44,6 +45,7 @@ public class RhinoFunctionResultChecker implements RhinoResultChecker {
@Override @Override
public void runChecks(Context context, Scriptable scope) throws Exception { public void runChecks(Context context, Scriptable scope) throws Exception {
Object result = evaluateFunction(context, scope); Object result = evaluateFunction(context, scope);
flushSystemOut(context, scope);
assertResultValid(result); assertResultValid(result);
} }
@@ -17,6 +17,7 @@
package org.jetbrains.k2js.test.rhino; package org.jetbrains.k2js.test.rhino;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.k2js.facade.K2JSTranslator;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
@@ -44,12 +45,15 @@ public final class RhinoSystemOutputChecker implements RhinoResultChecker {
+ "END_OF_EXPECTED\n", trimmedExpected.equals(trimmedActual)); + "END_OF_EXPECTED\n", trimmedExpected.equals(trimmedActual));
} }
@NotNull
private static String getSystemOutput(@NotNull Context context, @NotNull Scriptable scope) { private static String getSystemOutput(@NotNull Context context, @NotNull Scriptable scope) {
Object output = context.evaluateString(scope, "Kotlin.System.output()", "test", 0, null); Object output = context.evaluateString(scope, K2JSTranslator.GET_SYSTEM_OUT, "test", 0, null);
RhinoUtils.flushSystemOut(context, scope);
assertTrue("Output should be a string.", output instanceof String); assertTrue("Output should be a string.", output instanceof String);
return (String) output; return (String) output;
} }
@NotNull
public static String trimSpace(@NotNull String s) { public static String trimSpace(@NotNull String s) {
String[] choppedUpString = s.trim().split("\\s"); String[] choppedUpString = s.trim().split("\\s");
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -16,66 +16,135 @@
package org.jetbrains.k2js.test.rhino; package org.jetbrains.k2js.test.rhino;
import closurecompiler.internal.com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.config.EcmaVersion;
import org.jetbrains.k2js.facade.K2JSTranslator;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import java.io.FileReader; import java.io.FileReader;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.jetbrains.jet.utils.ExceptionUtils.rethrow;
import static org.jetbrains.k2js.test.BasicTest.pathToTestFilesRoot;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
*/ */
public final class RhinoUtils { public final class RhinoUtils {
public static final String KOTLIN_JS_LIB_COMMON = pathToTestFilesRoot() + "kotlin_lib.js";
private static final String KOTLIN_JS_LIB_ECMA_3 = pathToTestFilesRoot() + "kotlin_lib_ecma3.js";
private static final String KOTLIN_JS_LIB_ECMA_5 = pathToTestFilesRoot() + "kotlin_lib_ecma5.js";
private RhinoUtils() { private RhinoUtils() {
} }
private static void runFileWithRhino(@NotNull String inputFile, private static void runFileWithRhino(@NotNull String inputFile,
@NotNull Context context, @NotNull Context context,
@NotNull Scriptable scope) throws Exception { @NotNull Scriptable scope) throws Exception {
FileReader reader = new FileReader(inputFile); FileReader reader = new FileReader(inputFile);
try { try {
context.evaluateReader(scope, reader, inputFile, 1, null); context.evaluateReader(scope, reader, inputFile, 1, null);
} finally { }
finally {
reader.close(); reader.close();
} }
} }
public static void runRhinoTest(@NotNull List<String> fileNames, public static void runRhinoTest(@NotNull List<String> fileNames,
@NotNull RhinoResultChecker checker) throws Exception { @NotNull RhinoResultChecker checker) throws Exception {
runRhinoTest(fileNames, checker, null, EcmaVersion.defaultVersion()); runRhinoTest(fileNames, checker, null, EcmaVersion.defaultVersion());
} }
public static void runRhinoTest(@NotNull List<String> fileNames, public static void runRhinoTest(@NotNull List<String> fileNames,
@NotNull RhinoResultChecker checker, @NotNull RhinoResultChecker checker,
@Nullable Map<String, Object> variables, @Nullable Map<String, Object> variables,
@NotNull EcmaVersion ecmaVersion) throws Exception { @NotNull EcmaVersion ecmaVersion) throws Exception {
Context context = createContext(ecmaVersion);
try {
Scriptable scope = getScope(ecmaVersion, context);
putGlobalVariablesIntoScope(scope, variables);
for (String filename : fileNames) {
runFileWithRhino(filename, context, scope);
}
checker.runChecks(context, scope);
}
finally {
Context.exit();
}
}
@NotNull
private static Scriptable getScope(@NotNull EcmaVersion version, @NotNull Context context) {
ScriptableObject scope = context.initStandardObjects(null, false);
scope.setParentScope(getParentScope(version, context));
return scope;
}
@NotNull
private static Scriptable getParentScope(@NotNull EcmaVersion version, @NotNull Context context) {
Scriptable parentScope = versionToScope.get(version);
if (parentScope == null) {
parentScope = initScope(version, context);
versionToScope.put(version, parentScope);
}
return parentScope;
}
@NotNull
private static Scriptable initScope(@NotNull EcmaVersion version, @NotNull Context context) {
ScriptableObject scope = context.initStandardObjects();
try {
runFileWithRhino(getKotlinLibFile(version), context, scope);
runFileWithRhino(KOTLIN_JS_LIB_COMMON, context, scope);
}
catch (Exception e) {
throw rethrow(e);
}
scope.sealObject();
return scope;
}
//TODO:
@NotNull
private static Context createContext(@NotNull EcmaVersion ecmaVersion) {
Context context = Context.enter(); Context context = Context.enter();
if (ecmaVersion == EcmaVersion.v5) { if (ecmaVersion == EcmaVersion.v5) {
// actually, currently, doesn't matter because dart doesn't produce js 1.8 code (expression closures) // actually, currently, doesn't matter because dart doesn't produce js 1.8 code (expression closures)
context.setLanguageVersion(Context.VERSION_1_8); context.setLanguageVersion(Context.VERSION_1_8);
} }
return context;
}
Scriptable scope = context.initStandardObjects(); private static void putGlobalVariablesIntoScope(@NotNull Scriptable scope, @Nullable Map<String, Object> variables) {
if (variables != null) { if (variables == null) {
Set<Map.Entry<String,Object>> entries = variables.entrySet(); return;
for (Map.Entry<String, Object> entry : entries) {
String name = entry.getKey();
Object value = entry.getValue();
scope.put(name, scope, value);
}
} }
for (String filename : fileNames) { Set<Map.Entry<String, Object>> entries = variables.entrySet();
runFileWithRhino(filename, context, scope); for (Map.Entry<String, Object> entry : entries) {
String name = entry.getKey();
Object value = entry.getValue();
scope.put(name, scope, value);
} }
checker.runChecks(context, scope); }
Context.exit();
@NotNull
private static final Map<EcmaVersion, Scriptable> versionToScope = Maps.newHashMap();
@NotNull
public static String getKotlinLibFile(@NotNull EcmaVersion ecmaVersion) {
return ecmaVersion == EcmaVersion.v5 ? KOTLIN_JS_LIB_ECMA_5 : KOTLIN_JS_LIB_ECMA_3;
}
static void flushSystemOut(@NotNull Context context, @NotNull Scriptable scope) {
context.evaluateString(scope, K2JSTranslator.FLUSH_SYSTEM_OUT, "test", 0, null);
} }
} }
@@ -20,6 +20,7 @@ import closurecompiler.internal.com.google.common.collect.Maps;
import com.google.dart.compiler.backend.js.ast.JsProgram; import com.google.dart.compiler.backend.js.ast.JsProgram;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.config.EcmaVersion;
import org.jetbrains.k2js.facade.K2JSTranslator; import org.jetbrains.k2js.facade.K2JSTranslator;
@@ -29,7 +30,6 @@ import org.jetbrains.k2js.test.config.TestConfig;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -46,21 +46,16 @@ public final class TranslationUtils {
@NotNull @NotNull
private static final Map<EcmaVersion, K2JSTranslator> translators = Maps.newHashMap(); private static final Map<EcmaVersion, K2JSTranslator> translators = Maps.newHashMap();
public static void translateFile(@NotNull Project project, @NotNull String inputFile,
@NotNull String outputFile, @NotNull MainCallParameters mainCallParameters, @NotNull EcmaVersion version) throws Exception {
translateFiles(project, Collections.singletonList(inputFile), outputFile, mainCallParameters, version, null);
}
public static void translateFiles(@NotNull Project project, @NotNull List<String> inputFiles, public static void translateFiles(@NotNull Project project, @NotNull List<String> inputFiles,
@NotNull String outputFile, @NotNull String outputFile,
@NotNull MainCallParameters mainCallParameters, @NotNull MainCallParameters mainCallParameters,
@NotNull EcmaVersion version, @NotNull EcmaVersion version,
List<String> rawStatements) throws Exception { @Nullable List<String> rawStatements) throws Exception {
List<JetFile> psiFiles = createPsiFileList(inputFiles, project); List<JetFile> psiFiles = createPsiFileList(inputFiles, project);
JsProgram program = getTranslator(project, version).generateProgram(psiFiles, mainCallParameters, rawStatements); JsProgram program = getTranslator(project, version).generateProgram(psiFiles, mainCallParameters, rawStatements);
FileWriter writer = new FileWriter(new File(outputFile)); FileWriter writer = new FileWriter(new File(outputFile));
try { try {
writer.write(CodeGenerator.toString(program, null)); writer.write(CodeGenerator.generateProgramToString(program, null));
} }
finally { finally {
writer.close(); writer.close();
@@ -20,13 +20,13 @@ import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.JsProgram; import com.google.dart.compiler.backend.js.ast.JsProgram;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS; import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
import org.jetbrains.k2js.config.Config; import org.jetbrains.k2js.config.Config;
import org.jetbrains.k2js.facade.exceptions.TranslationException; import org.jetbrains.k2js.facade.exceptions.TranslationException;
import org.jetbrains.k2js.generate.CodeGenerator;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.utils.JetFileUtils; import org.jetbrains.k2js.utils.JetFileUtils;
@@ -37,6 +37,7 @@ import java.util.List;
import static org.jetbrains.k2js.facade.FacadeUtils.parseString; import static org.jetbrains.k2js.facade.FacadeUtils.parseString;
import static org.jetbrains.k2js.facade.FacadeUtils.writeCodeToFile; import static org.jetbrains.k2js.facade.FacadeUtils.writeCodeToFile;
import static org.jetbrains.k2js.generate.CodeGenerator.generateProgramToString;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -45,6 +46,9 @@ import static org.jetbrains.k2js.facade.FacadeUtils.writeCodeToFile;
*/ */
public final class K2JSTranslator { public final class K2JSTranslator {
public static final String FLUSH_SYSTEM_OUT = "Kotlin.System.flush();\n";
public static final String GET_SYSTEM_OUT = "Kotlin.System.output();\n";
public static void translateWithMainCallParametersAndSaveToFile(@NotNull MainCallParameters mainCall, public static void translateWithMainCallParametersAndSaveToFile(@NotNull MainCallParameters mainCall,
@NotNull List<JetFile> files, @NotNull List<JetFile> files,
@NotNull String outputPath, @NotNull String outputPath,
@@ -62,22 +66,20 @@ public final class K2JSTranslator {
this.config = config; this.config = config;
} }
//TODO: web demo related method //NOTE: web demo related method
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
@NotNull @NotNull
public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) throws TranslationException { public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) throws TranslationException {
JetFile file = JetFileUtils.createPsiFile("test", programText, getProject()); JetFile file = JetFileUtils.createPsiFile("test", programText, getProject());
String programCode = generateProgramCode(file, MainCallParameters.mainWithArguments(parseString(argumentsString)), null) + "\n"; String programCode = generateProgramCode(file, MainCallParameters.mainWithArguments(parseString(argumentsString)), null) + "\n";
String flushOutput = "Kotlin.System.flush();\n"; return FLUSH_SYSTEM_OUT + programCode + GET_SYSTEM_OUT;
String programOutput = "Kotlin.System.output();\n";
return flushOutput + programCode + programOutput;
} }
@NotNull @NotNull
public String generateProgramCode(@NotNull JetFile file, @NotNull MainCallParameters mainCallParameters, List<String> rawStatements) throws TranslationException { public String generateProgramCode(@NotNull JetFile file, @NotNull MainCallParameters mainCallParameters,
@Nullable List<String> rawStatements) throws TranslationException {
JsProgram program = generateProgram(Arrays.asList(file), mainCallParameters, rawStatements); JsProgram program = generateProgram(Arrays.asList(file), mainCallParameters, rawStatements);
CodeGenerator generator = new CodeGenerator(); return generateProgramToString(program, rawStatements);
return generator.generateToString(program, rawStatements);
} }
@NotNull @NotNull
@@ -85,14 +87,13 @@ public final class K2JSTranslator {
throws TranslationException { throws TranslationException {
List<String> rawStatements = Lists.newArrayList(); List<String> rawStatements = Lists.newArrayList();
JsProgram program = generateProgram(files, mainCallParameters, rawStatements); JsProgram program = generateProgram(files, mainCallParameters, rawStatements);
CodeGenerator generator = new CodeGenerator(); return generateProgramToString(program, rawStatements);
return generator.generateToString(program, rawStatements);
} }
@NotNull @NotNull
public JsProgram generateProgram(@NotNull List<JetFile> filesToTranslate, public JsProgram generateProgram(@NotNull List<JetFile> filesToTranslate,
@NotNull MainCallParameters mainCallParameters, @NotNull MainCallParameters mainCallParameters,
List<String> rawStatements) @Nullable List<String> rawStatements)
throws TranslationException { throws TranslationException {
JetStandardLibrary.initialize(config.getProject()); JetStandardLibrary.initialize(config.getProject());
BindingContext bindingContext = AnalyzerFacadeForJS.analyzeFilesAndCheckErrors(filesToTranslate, config); BindingContext bindingContext = AnalyzerFacadeForJS.analyzeFilesAndCheckErrors(filesToTranslate, config);
@@ -21,10 +21,8 @@ import com.google.dart.compiler.backend.js.ast.JsProgram;
import com.google.dart.compiler.util.DefaultTextOutput; import com.google.dart.compiler.util.DefaultTextOutput;
import com.google.dart.compiler.util.TextOutput; import com.google.dart.compiler.util.TextOutput;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
@@ -35,31 +33,16 @@ public final class CodeGenerator {
@NotNull @NotNull
private final TextOutput output = new DefaultTextOutput(false); private final TextOutput output = new DefaultTextOutput(false);
public CodeGenerator() { private CodeGenerator() {
}
public static void toFile(@NotNull String outputFile, @NotNull JsProgram program) throws IOException {
CodeGenerator generator = new CodeGenerator();
generator.generateToFile(program, new File(outputFile));
} }
@NotNull @NotNull
public static String toString(@NotNull JsProgram program, List<String> rawStatements) { public static String generateProgramToString(@NotNull JsProgram program, @Nullable List<String> rawStatements) {
return (new CodeGenerator()).generateToString(program, rawStatements); return (new CodeGenerator()).generateToString(program, rawStatements);
} }
public void generateToFile(@NotNull JsProgram program, @NotNull File file) throws IOException {
generateCode(program);
FileWriter writer = new FileWriter(file);
try {
writer.write(output.toString());
} finally {
writer.close();
}
}
@NotNull @NotNull
public String generateToString(@NotNull JsProgram program, List<String> rawStatements) { private String generateToString(@NotNull JsProgram program, List<String> rawStatements) {
generateCode(program); generateCode(program);
if (rawStatements != null) { if (rawStatements != null) {
for (String statement : rawStatements) { for (String statement : rawStatements) {