From c89a94d2660097addfc98b743447a4b246e9d59c Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Thu, 23 Nov 2023 10:42:10 -0600 Subject: [PATCH 1/8] Current iteration of test and compiler pass Note that the pass still runs too early, but we can't easily run it earlier because we're dependent on inlineFunction rewriting params to the lookup/register calls. --- .../src/it/registry-sample/pom.xml | 43 ++++++ .../it/registry-sample/src/main/java/app.js | 3 + .../it/registry-sample/src/main/java/java.js | 16 +++ .../registry-sample/src/main/java/language.js | 14 ++ .../registry-sample/src/main/java/library.js | 8 ++ .../registry-sample/src/main/java/python.js | 16 +++ .../registry-sample/src/main/java/registry.js | 16 +++ .../com/vertispan/j2cl/tools/Closure.java | 2 + .../ConvertServiceLoaderProperties.java | 97 +++++++++++++ .../tools/closure/JsCheckerPassConfig.java | 136 ++++++++++++++++++ .../closure/ServiceLoadingPassConfig.java | 32 +++++ 11 files changed, 383 insertions(+) create mode 100644 j2cl-maven-plugin/src/it/registry-sample/pom.xml create mode 100644 j2cl-maven-plugin/src/it/registry-sample/src/main/java/app.js create mode 100644 j2cl-maven-plugin/src/it/registry-sample/src/main/java/java.js create mode 100644 j2cl-maven-plugin/src/it/registry-sample/src/main/java/language.js create mode 100644 j2cl-maven-plugin/src/it/registry-sample/src/main/java/library.js create mode 100644 j2cl-maven-plugin/src/it/registry-sample/src/main/java/python.js create mode 100644 j2cl-maven-plugin/src/it/registry-sample/src/main/java/registry.js create mode 100644 j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java create mode 100644 j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/JsCheckerPassConfig.java create mode 100644 j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ServiceLoadingPassConfig.java diff --git a/j2cl-maven-plugin/src/it/registry-sample/pom.xml b/j2cl-maven-plugin/src/it/registry-sample/pom.xml new file mode 100644 index 00000000..fab7117b --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + + registry-sample + registry-sample + 1.0 + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + build + + + + + + java + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + 1.8 + 1.8 + + + + + + diff --git a/j2cl-maven-plugin/src/it/registry-sample/src/main/java/app.js b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/app.js new file mode 100644 index 00000000..4a3bf717 --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/app.js @@ -0,0 +1,3 @@ +goog.require('hello'); + +setTimeout(()=>console.log(sayHello()), 100); \ No newline at end of file diff --git a/j2cl-maven-plugin/src/it/registry-sample/src/main/java/java.js b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/java.js new file mode 100644 index 00000000..f55d4fb1 --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/java.js @@ -0,0 +1,16 @@ +goog.provide('java') +goog.require('language') +goog.require('serviceloader') + +/** + * + * @constructor + * @implements ProgrammingLanguage + */ +function JavaLanguage() { + +} +JavaLanguage.prototype.getNameStr = function() { + return "Java"; +}; +register("java$j2cl$service$loader$key", () => new JavaLanguage()); diff --git a/j2cl-maven-plugin/src/it/registry-sample/src/main/java/language.js b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/language.js new file mode 100644 index 00000000..3f20101f --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/language.js @@ -0,0 +1,14 @@ +goog.provide('language') + +/** + * + * @interface + */ +function ProgrammingLanguage() { + +} + +/** + * @return {string} + */ +ProgrammingLanguage.prototype.getNameStr = function() {}; \ No newline at end of file diff --git a/j2cl-maven-plugin/src/it/registry-sample/src/main/java/library.js b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/library.js new file mode 100644 index 00000000..369ff0ba --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/library.js @@ -0,0 +1,8 @@ +goog.provide('hello') +goog.require('language') +goog.require('serviceloader') + +function sayHello() { + var defaultLanguage = lookupDefault(); + return "Hello, " + defaultLanguage.getNameStr() + "!"; +} diff --git a/j2cl-maven-plugin/src/it/registry-sample/src/main/java/python.js b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/python.js new file mode 100644 index 00000000..f9b6ac4a --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/python.js @@ -0,0 +1,16 @@ +goog.provide('py') +goog.require('language') +goog.require('serviceloader') + +/** + * + * @constructor + * @implements ProgrammingLanguage + */ +function PythonLanguage() { + +} +PythonLanguage.prototype.getNameStr = function() { + return "Python"; +}; +register("python$j2cl$service$loader$key", () => new PythonLanguage()); \ No newline at end of file diff --git a/j2cl-maven-plugin/src/it/registry-sample/src/main/java/registry.js b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/registry.js new file mode 100644 index 00000000..a1091813 --- /dev/null +++ b/j2cl-maven-plugin/src/it/registry-sample/src/main/java/registry.js @@ -0,0 +1,16 @@ +goog.provide('serviceloader') + +var map = {}; +function register(key, creator) { + map[key] = creator; +} +function lookup(key) { + return map[key](); +} + +// Specify a default, and allow it to be overridden at build time +/** @define {string} */ +const exampleDefault = goog.define('exampleDefault', 'python$j2cl$service$loader$key'); +function lookupDefault() { + return lookup(exampleDefault); +} \ No newline at end of file diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Closure.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Closure.java index de491c44..e610e537 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Closure.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Closure.java @@ -5,6 +5,7 @@ import com.vertispan.j2cl.build.DiskCache; import com.vertispan.j2cl.build.task.BuildLog; import com.vertispan.j2cl.build.task.Input; +import com.vertispan.j2cl.tools.closure.ServiceLoadingPassConfig; import javax.annotation.Nullable; import java.io.File; @@ -201,6 +202,7 @@ static class InProcessJsCompRunner extends CommandLineRunner { super(args); this.compiler = compiler; this.compiler.setErrorManager(new SortingErrorManager(Collections.singleton(new LoggingErrorReportGenerator(compiler, log)))); + this.compiler.setPassConfig(new ServiceLoadingPassConfig(createOptions())); this.exportTestFunctions = exportTestFunctions; this.checkAssertions = checkAssertions; setExitCodeReceiver(exitCode -> { diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java new file mode 100644 index 00000000..cccde35b --- /dev/null +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java @@ -0,0 +1,97 @@ +package com.vertispan.j2cl.tools.closure; + +import com.google.javascript.jscomp.AbstractCompiler; +import com.google.javascript.jscomp.CompilerPass; +import com.google.javascript.jscomp.GatherGetterAndSetterProperties; +import com.google.javascript.jscomp.NodeTraversal; +import com.google.javascript.jscomp.parsing.parser.FeatureSet; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.TokenStream; + +/** + * Modified copy of ConvertToDottedProperties. Changes from original: + * + *

+ * See mailing list discussion. + */ +public class ConvertServiceLoaderProperties extends NodeTraversal.AbstractPostOrderCallback implements CompilerPass { + + private final AbstractCompiler compiler; + + ConvertServiceLoaderProperties(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + //TODO consider removing this (or this comment) + GatherGetterAndSetterProperties.update(this.compiler, externs, root); + System.err.println("ConvertServiceLoaderProperties running"); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getToken()) { + case OPTCHAIN_GETELEM: + case GETELEM: + Node left = n.getFirstChild(); + Node right = left.getNext(); + if (right.isStringLit() && right.getString().endsWith("$j2cl$service$loader$key") && isValidPropertyName(FeatureSet.ES3, right.getString())) { + left.detach(); + right.detach(); + Node newGetProp = + n.isGetElem() + ? IR.getprop(left, right.getString()) + : (n.isOptionalChainStart() + ? IR.startOptChainGetprop(left, right.getString()) + : IR.continueOptChainGetprop(left, right.getString())); + n.replaceWith(newGetProp); + compiler.reportChangeToEnclosingScope(newGetProp); + } + break; + default: + break; + } + } + + private static boolean isValidPropertyName(FeatureSet mode, String name) { + if (isValidSimpleName(name)) { + return true; + } else { + return mode.has(FeatureSet.Feature.KEYWORDS_AS_PROPERTIES) && TokenStream.isKeyword(name); + } + } + static boolean isValidSimpleName(String name) { + return TokenStream.isJSIdentifier(name) + && !TokenStream.isKeyword(name) + // no Unicode escaped characters - some browsers are less tolerant + // of Unicode characters that might be valid according to the + // language spec. + // Note that by this point, Unicode escapes have been converted + // to UTF-16 characters, so we're only searching for character + // values, not escapes. + && isLatin(name); + } + static boolean isLatin(String s) { + int len = s.length(); + for (int index = 0; index < len; index++) { + char c = s.charAt(index); + if (c > LARGEST_BASIC_LATIN) { + return false; + } + } + return true; + } + static final char LARGEST_BASIC_LATIN = 0x7f; + +} diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/JsCheckerPassConfig.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/JsCheckerPassConfig.java new file mode 100644 index 00000000..50175e51 --- /dev/null +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/JsCheckerPassConfig.java @@ -0,0 +1,136 @@ +//package com.vertispan.j2cl.tools.closure; +// +//import com.google.common.collect.ImmutableList; +//import com.google.javascript.jscomp.CheckJSDoc; +//import com.google.javascript.jscomp.CheckSuper; +//import com.google.javascript.jscomp.ClosureCheckModule; +//import com.google.javascript.jscomp.ClosureRewriteClass; +//import com.google.javascript.jscomp.CombinedCompilerPass; +//import com.google.javascript.jscomp.CompilerOptions; +//import com.google.javascript.jscomp.DefaultPassConfig; +//import com.google.javascript.jscomp.GatherModuleMetadata; +//import com.google.javascript.jscomp.NodeTraversal; +//import com.google.javascript.jscomp.PassConfig; +//import com.google.javascript.jscomp.PassFactory; +//import com.google.javascript.jscomp.PassListBuilder; +//import com.google.javascript.jscomp.ScopedAliases; +//import com.google.javascript.jscomp.ijs.ConvertToTypedInterface; +//import com.google.javascript.jscomp.lint.CheckDuplicateCase; +//import com.google.javascript.jscomp.lint.CheckEmptyStatements; +//import com.google.javascript.jscomp.lint.CheckEnums; +//import com.google.javascript.jscomp.lint.CheckInterfaces; +//import com.google.javascript.jscomp.lint.CheckJSDocStyle; +//import com.google.javascript.jscomp.lint.CheckMissingSemicolon; +//import com.google.javascript.jscomp.lint.CheckPrimitiveAsObject; +//import com.google.javascript.jscomp.lint.CheckPrototypeProperties; +//import com.google.javascript.jscomp.lint.CheckProvidesSorted; +//import com.google.javascript.jscomp.lint.CheckRequiresSorted; +//import com.google.javascript.jscomp.lint.CheckUnusedLabels; +//import com.google.javascript.jscomp.lint.CheckUselessBlocks; +// +//public class JsCheckerPassConfig extends PassConfig.PassConfigDelegate { +// +// private final JsCheckerState state; +// private final PassListBuilder checks; +// +// JsCheckerPassConfig(JsCheckerState state, CompilerOptions options) { +// super(new DefaultPassConfig(options)); +// this.state = state; +// this.checks = new PassListBuilder(options); +// +// this.checks.maybeAdd(gatherModuleMetadataPass()); +// this.checks.maybeAdd(earlyLintChecks()); +// this.checks.maybeAdd(scopedAliases()); +// this.checks.maybeAdd(closureRewriteClass()); +// this.checks.maybeAdd(lateLintChecks()); +// this.checks.maybeAdd(ijsGeneration()); +// } +// +// @Override +// protected PassListBuilder getChecks() { +// return checks; +// } +// +// @Override +// protected PassListBuilder getOptimizations() { +// return new PassListBuilder(options); +// } +// +// private PassFactory gatherModuleMetadataPass() { +// return PassFactory.builder() +// .setName("gather module metadata") +// .setInternalFactory( +// (compiler) -> +// new GatherModuleMetadata( +// compiler, +// compiler.getOptions().getProcessCommonJSModules(), +// compiler.getOptions().getModuleResolutionMode())) +// .build(); +// } +// +// private PassFactory earlyLintChecks() { +// return PassFactory.builder() +// .setName("earlyLintChecks") +// .setInternalFactory( +// (compiler) -> +// new CombinedCompilerPass( +// compiler, +// ImmutableList.of( +// new CheckDuplicateCase(compiler), +// new CheckEmptyStatements(compiler), +// new CheckEnums(compiler), +// new CheckJSDocStyle(compiler), +// new CheckJSDoc(compiler), +// new CheckMissingSemicolon(compiler), +// new CheckSuper(compiler), +// new CheckPrimitiveAsObject(compiler), +// new CheckProvidesSorted(CheckProvidesSorted.Mode.COLLECT_AND_REPORT), +// new CheckRequiresSorted(CheckRequiresSorted.Mode.COLLECT_AND_REPORT), +// new CheckUnusedLabels(compiler), +// new CheckUselessBlocks(compiler), +// new ClosureCheckModule(compiler, compiler.getModuleMetadataMap()), +// new CheckSetTestOnly(state, compiler), +// new CheckStrictDeps.FirstPass(state, compiler)))) +// .build(); +// } +// +// private PassFactory scopedAliases() { +// return PassFactory.builder() +// .setName("scopedAliases") +// .setInternalFactory( +// (compiler) -> +// new ScopedAliases( +// compiler, +// /*preprocessorSymbolTable=*/ null, +// compiler.getOptions().getAliasTransformationHandler())) +// .build(); +// } +// +// private PassFactory closureRewriteClass() { +// return PassFactory.builder() +// .setName("closureRewriteClass") +// .setInternalFactory((compiler) -> new ClosureRewriteClass(compiler)) +// .build(); +// } +// +// private PassFactory lateLintChecks() { +// return PassFactory.builder() +// .setName("lateLintChecks") +// .setInternalFactory( +// (compiler) -> +// new CombinedCompilerPass( +// compiler, +// ImmutableList.of( +// new CheckInterfaces(compiler), +// new CheckPrototypeProperties(compiler), +// new CheckStrictDeps.SecondPass(state, compiler)))) +// .build(); +// } +// +// private PassFactory ijsGeneration() { +// return PassFactory.builder() +// .setName("ijsGeneration") +// .setInternalFactory((compiler) -> new ConvertToTypedInterface(compiler)) +// .build(); +// } +//} diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ServiceLoadingPassConfig.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ServiceLoadingPassConfig.java new file mode 100644 index 00000000..3eafaa0c --- /dev/null +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ServiceLoadingPassConfig.java @@ -0,0 +1,32 @@ +package com.vertispan.j2cl.tools.closure; + +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.DefaultPassConfig; +import com.google.javascript.jscomp.PassConfig; +import com.google.javascript.jscomp.PassFactory; +import com.google.javascript.jscomp.PassListBuilder; +import com.google.javascript.jscomp.PassNames; + +public class ServiceLoadingPassConfig extends PassConfig.PassConfigDelegate { + + private final PassFactory convertServiceLoaderProperties = + PassFactory.builder() + .setName("ConvertServiceLoaderProperties") + .setRunInFixedPointLoop(true) + .setInternalFactory(ConvertServiceLoaderProperties::new) + .build(); + + public ServiceLoadingPassConfig(CompilerOptions options) { + super(new DefaultPassConfig(options)); + } + + @Override + protected PassListBuilder getOptimizations() { + PassListBuilder optimizations = super.getOptimizations(); + optimizations.addBefore(convertServiceLoaderProperties, PassNames.AFTER_EARLY_OPTIMIZATION_LOOP); + optimizations.addBefore(convertServiceLoaderProperties, PassNames.AFTER_MAIN_OPTIMIZATIONS); +// optimizations.addAfter(convertServiceLoaderProperties, PassNames.PEEPHOLE_OPTIMIZATIONS); +// optimizations.findByName() + return optimizations; + } +} From 27f1829ff74f88cf09f0372bdd369fc9b78e3aaa Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Sat, 25 Nov 2023 11:54:22 -0600 Subject: [PATCH 2/8] Simplify the pass, run more often, doesn't help --- .../ConvertServiceLoaderProperties.java | 45 +++++++++---------- .../closure/ServiceLoadingPassConfig.java | 2 + 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java index cccde35b..00483403 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/closure/ConvertServiceLoaderProperties.java @@ -2,13 +2,15 @@ import com.google.javascript.jscomp.AbstractCompiler; import com.google.javascript.jscomp.CompilerPass; -import com.google.javascript.jscomp.GatherGetterAndSetterProperties; import com.google.javascript.jscomp.NodeTraversal; import com.google.javascript.jscomp.parsing.parser.FeatureSet; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; +import java.util.Objects; + /** * Modified copy of ConvertToDottedProperties. Changes from original: *