Skip to content

Commit

Permalink
js: introduce setting to choose module type
Browse files Browse the repository at this point in the history
  • Loading branch information
konsoletyper committed Dec 22, 2023
1 parent 8c344b3 commit 22f5d5f
Show file tree
Hide file tree
Showing 31 changed files with 477 additions and 49 deletions.
23 changes: 23 additions & 0 deletions core/src/main/java/org/teavm/backend/javascript/JSModuleType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2023 Alexey Andreev.
*
* 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.teavm.backend.javascript;

public enum JSModuleType {
COMMON_JS,
UMD,
NONE,
ES2015
}
131 changes: 122 additions & 9 deletions core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
private final Map<String, String> importedModules = new LinkedHashMap<>();
private JavaScriptTemplateFactory templateFactory;
private JSModuleType moduleType = JSModuleType.UMD;

@Override
public List<ClassHolderTransformer> getTransformers() {
Expand Down Expand Up @@ -214,6 +215,10 @@ public void setStrict(boolean strict) {
this.strict = strict;
}

public void setModuleType(JSModuleType moduleType) {
this.moduleType = moduleType;
}

@Override
public boolean requiresRegisterAllocation() {
return true;
Expand Down Expand Up @@ -403,11 +408,12 @@ public String importModule(String name) {
renderer.renderStringConstants();
renderer.renderCompatibilityStubs();
for (var entry : controller.getEntryPoints().entrySet()) {
rememberingWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).ws().append("=").ws();
var alias = "$rt_export_" + entry.getKey();
var ref = entry.getValue().getMethod();
rememberingWriter.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref);
rememberingWriter.append("let ").appendFunction(alias).ws().append("=").ws()
.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref);
rememberingWriter.append(");").newLine();
rememberingWriter.appendFunction("$rt_exports").append(".").append(entry.getKey()).append(".")
rememberingWriter.appendFunction(alias).append(".")
.append("javaException").ws().append("=").ws().appendFunction("$rt_javaException")
.append(";").newLine();
}
Expand Down Expand Up @@ -450,18 +456,30 @@ public String importModule(String name) {

private void printWrapperStart(SourceWriter writer) {
writer.append("\"use strict\";").newLine();
printUmdStart(writer);
writer.append("function(").appendFunction("$rt_exports");
for (var moduleName : importedModules.values()) {
writer.append(",").ws().appendFunction(moduleName);
}
writer.append(")").appendBlockStart();
printModuleStart(writer);
}

private String importModule(String name) {
return importedModules.computeIfAbsent(name, n -> "$rt_imported_" + importedModules.size());
}

private void printModuleStart(SourceWriter writer) {
switch (moduleType) {
case UMD:
printUmdStart(writer);
break;
case COMMON_JS:
printCommonJsStart(writer);
break;
case NONE:
printIIFStart(writer);
break;
case ES2015:
printES2015Start(writer);
break;
}
}

private void printUmdStart(SourceWriter writer) {
writer.append("(function(module)").appendBlockStart();
writer.appendIf().append("typeof define").ws().append("===").ws().append("'function'")
Expand Down Expand Up @@ -501,12 +519,107 @@ private void printUmdStart(SourceWriter writer) {
writer.append(");").softNewLine();
writer.appendBlockEnd();
writer.outdent().append("}(");

writer.append("function(").appendFunction("$rt_exports");
for (var moduleName : importedModules.values()) {
writer.append(",").ws().appendFunction(moduleName);
}
writer.append(")").appendBlockStart();
}

private void printIIFStart(SourceWriter writer) {
for (var exportedName : controller.getEntryPoints().keySet()) {
writer.append("var ").appendGlobal(exportedName).append(";").softNewLine();
}
for (var moduleName : importedModules.values()) {
writer.append(",").ws().appendFunction(moduleName);
}
writer.append("(function()").appendBlockStart();

for (var entry : importedModules.entrySet()) {
var moduleName = entry.getKey();
var alias = entry.getValue();
writer.append("let ").appendFunction(alias).ws().append('=').ws().append("this[\"")
.append(RenderingUtil.escapeString(moduleName)).append("\"];").softNewLine();
}
}

private void printCommonJsStart(SourceWriter writer) {
for (var entry : importedModules.entrySet()) {
var moduleName = entry.getKey();
var alias = entry.getValue();
writer.append("let ").appendFunction(alias).ws().append('=').ws().append("require(\"")
.append(RenderingUtil.escapeString(moduleName)).append("\");").softNewLine();
}
}

private void printES2015Start(SourceWriter writer) {
for (var entry : importedModules.entrySet()) {
var moduleName = entry.getKey();
var alias = entry.getValue();
writer.append("import").ws().append("*").ws().append("as ").appendFunction(alias).ws()
.append("from").ws().append("\"")
.append(RenderingUtil.escapeString(moduleName)).append("\";").softNewLine();
}
}

private void printWrapperEnd(SourceWriter writer) {
switch (moduleType) {
case UMD:
printUmdEnd(writer);
break;
case COMMON_JS:
printCommonJsEnd(writer);
break;
case NONE:
printIFFEnd(writer);
break;
case ES2015:
printES2015End(writer);
break;
}
}

private void printUmdEnd(SourceWriter writer) {
for (var export : controller.getEntryPoints().keySet()) {
writer.appendFunction("$rt_exports").append(".").append(export).ws().append("=").ws()
.appendFunction("$rt_export_" + export);
}
writer.outdent().append("}));").newLine();
}

private void printCommonJsEnd(SourceWriter writer) {
for (var export : controller.getEntryPoints().keySet()) {
writer.appendFunction("exports.").append(export).ws().append("=").ws()
.appendFunction("$rt_export_" + export);
}
writer.outdent().append("}));").newLine();
}

private void printIFFEnd(SourceWriter writer) {
for (var exportedName : controller.getEntryPoints().keySet()) {
writer.appendGlobal(exportedName).ws().append("=").ws().appendFunction("$rt_export_" + exportedName)
.append(";").softNewLine();
}
writer.outdent().append("})();");
}

private void printES2015End(SourceWriter writer) {
if (controller.getEntryPoints().isEmpty()) {
return;
}
writer.append("export").ws().append("{").ws();
var first = true;
for (var exportedName : controller.getEntryPoints().keySet()) {
if (!first) {
writer.append(",").ws();
}
first = false;
writer.appendFunction("$rt_export_" + exportedName).append(" as ").append(exportedName);
}
writer.ws().append("};").softNewLine();
}

private void printStats(OutputSourceWriter writer, int totalSize) {
if (!Boolean.parseBoolean(System.getProperty("teavm.js.stats", "false"))) {
return;
Expand Down
11 changes: 10 additions & 1 deletion tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import org.teavm.jso.JSBodyImport;
import org.teavm.junit.AttachJavaScript;
import org.teavm.junit.EachTestCompiledSeparately;
import org.teavm.junit.JsModuleTest;
import org.teavm.junit.OnlyPlatform;
import org.teavm.junit.ServeJS;
import org.teavm.junit.SkipJVM;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform;
Expand All @@ -47,9 +49,16 @@ public void commonjs() {
assertEquals(23, runTestFunction());
}

@Test
@JsModuleTest
@ServeJS(from = "org/teavm/jso/test/es2015.js", as = "testModule.js")
public void es2015() {
assertEquals(23, runTestFunction());
}

@JSBody(
script = "return testModule.foo();",
imports = @JSBodyImport(alias = "testModule", fromModule = "testModule.js")
imports = @JSBodyImport(alias = "testModule", fromModule = "./testModule.js")
)
private static native int runTestFunction();
}
2 changes: 1 addition & 1 deletion tests/src/test/resources/org/teavm/jso/test/amdModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

define("testModule.js", [], () => {
define("./testModule.js", [], () => {
return {
foo() {
return 23;
Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/resources/org/teavm/jso/test/commonjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

function require(name) {
switch (name) {
case "testModule.js": {
case "./testModule.js": {
return {
foo() {
return 23;
Expand Down
19 changes: 19 additions & 0 deletions tests/src/test/resources/org/teavm/jso/test/es2015.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2023 Alexey Andreev.
*
* 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.
*/

export function foo() {
return 23;
}
32 changes: 28 additions & 4 deletions tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.teavm.backend.javascript.JSModuleType;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.ConsoleTeaVMToolLog;
import org.teavm.tooling.TeaVMProblemRenderer;
Expand Down Expand Up @@ -145,11 +146,10 @@ private static void setupOptions() {
.desc("Maximum heap size in megabytes (for C and WebAssembly)")
.build());
options.addOption(Option.builder()
.longOpt("max-toplevel-names")
.argName("number")
.longOpt("js-module-type")
.argName("module-type")
.hasArg()
.desc("Maximum number of names kept in top-level scope ("
+ "other will be put in a separate object. 10000 by default.")
.desc("JavaScript module type (umd, common-js, none, es2015).")
.build());
}

Expand Down Expand Up @@ -240,6 +240,30 @@ private void parseOutputOptions() {
private void parseGenerationOptions() {
tool.setObfuscated(commandLine.hasOption("m"));
tool.setStrict(commandLine.hasOption("strict"));
parseJsModuleOption();
}

private void parseJsModuleOption() {
if (!commandLine.hasOption("js-module-type")) {
return;
}
switch (commandLine.getOptionValue("js-module-type")) {
case "umd":
tool.setJsModuleType(JSModuleType.UMD);
break;
case "common-js":
tool.setJsModuleType(JSModuleType.COMMON_JS);
break;
case "none":
tool.setJsModuleType(JSModuleType.NONE);
break;
case "es2015":
tool.setJsModuleType(JSModuleType.ES2015);
break;
default:
System.err.print("Wrong JS module type level");
printUsage();
}
}

private void parseDebugOptions() {
Expand Down
7 changes: 7 additions & 0 deletions tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.teavm.backend.c.generate.CNameProvider;
import org.teavm.backend.c.generate.ShorteningFileNameProvider;
import org.teavm.backend.c.generate.SimpleFileNameProvider;
import org.teavm.backend.javascript.JSModuleType;
import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.backend.wasm.WasmRuntimeType;
import org.teavm.backend.wasm.WasmTarget;
Expand Down Expand Up @@ -75,6 +76,7 @@ public class TeaVMTool {
private TeaVMTargetType targetType = TeaVMTargetType.JAVASCRIPT;
private String targetFileName = "";
private boolean obfuscated = true;
private JSModuleType jsModuleType = JSModuleType.UMD;
private boolean strict;
private String mainClass;
private String entryPointName = "main";
Expand Down Expand Up @@ -129,6 +131,10 @@ public void setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
}

public void setJsModuleType(JSModuleType jsModuleType) {
this.jsModuleType = jsModuleType;
}

public void setStrict(boolean strict) {
this.strict = strict;
}
Expand Down Expand Up @@ -334,6 +340,7 @@ private TeaVMTarget prepareJavaScriptTarget() {
debugEmitter = debugInformationGenerated || sourceMapsFileGenerated
? new DebugInformationBuilder(referenceCache) : null;
javaScriptTarget.setDebugEmitter(debugEmitter);
javaScriptTarget.setModuleType(jsModuleType);

return javaScriptTarget;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.List;
import java.util.Properties;
import org.teavm.backend.javascript.JSModuleType;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
Expand Down Expand Up @@ -60,6 +61,8 @@ public interface BuildStrategy {

void setStrict(boolean strict);

void setJsModuleType(JSModuleType jsModuleType);

void setProperties(Properties properties);

void setTransformers(String[] transformers);
Expand Down
Loading

0 comments on commit 22f5d5f

Please sign in to comment.