mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
JS: supports module imports in JSBody
This commit is contained in:
parent
a97e0ef45c
commit
334e2829b3
|
@ -30,8 +30,10 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
|
@ -50,11 +52,14 @@ import org.teavm.backend.javascript.decompile.PreparedClass;
|
||||||
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
import org.teavm.backend.javascript.decompile.PreparedMethod;
|
||||||
import org.teavm.backend.javascript.rendering.Renderer;
|
import org.teavm.backend.javascript.rendering.Renderer;
|
||||||
import org.teavm.backend.javascript.rendering.RenderingContext;
|
import org.teavm.backend.javascript.rendering.RenderingContext;
|
||||||
|
import org.teavm.backend.javascript.rendering.RenderingUtil;
|
||||||
import org.teavm.backend.javascript.rendering.RuntimeRenderer;
|
import org.teavm.backend.javascript.rendering.RuntimeRenderer;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
|
import org.teavm.backend.javascript.spi.ModuleImporter;
|
||||||
|
import org.teavm.backend.javascript.spi.ModuleImporterContext;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
||||||
import org.teavm.cache.AstCacheEntry;
|
import org.teavm.cache.AstCacheEntry;
|
||||||
|
@ -68,6 +73,7 @@ import org.teavm.debugging.information.SourceLocation;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.dependency.DependencyType;
|
import org.teavm.dependency.DependencyType;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
@ -83,6 +89,7 @@ import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -120,6 +127,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private final Map<MethodReference, Injector> methodInjectors = new HashMap<>();
|
private final Map<MethodReference, Injector> methodInjectors = new HashMap<>();
|
||||||
private final List<Function<ProviderContext, Generator>> generatorProviders = new ArrayList<>();
|
private final List<Function<ProviderContext, Generator>> generatorProviders = new ArrayList<>();
|
||||||
private final List<Function<ProviderContext, Injector>> injectorProviders = new ArrayList<>();
|
private final List<Function<ProviderContext, Injector>> injectorProviders = new ArrayList<>();
|
||||||
|
private final List<Function<ProviderContext, ModuleImporter>> moduleImporterProviders = new ArrayList<>();
|
||||||
private final List<RendererListener> rendererListeners = new ArrayList<>();
|
private final List<RendererListener> rendererListeners = new ArrayList<>();
|
||||||
private DebugInformationEmitter debugEmitter;
|
private DebugInformationEmitter debugEmitter;
|
||||||
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||||
|
@ -131,6 +139,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private boolean strict;
|
private boolean strict;
|
||||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
||||||
|
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ClassHolderTransformer> getTransformers() {
|
public List<ClassHolderTransformer> getTransformers() {
|
||||||
|
@ -172,6 +181,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
injectorProviders.add(provider);
|
injectorProviders.add(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addModuleImporterProvider(Function<ProviderContext, ModuleImporter> provider) {
|
||||||
|
moduleImporterProviders.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether this TeaVM instance uses obfuscation when generating the JavaScript code.
|
* Specifies whether this TeaVM instance uses obfuscation when generating the JavaScript code.
|
||||||
*
|
*
|
||||||
|
@ -361,6 +375,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
|
|
||||||
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
|
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
|
||||||
List<PreparedClass> clsNodes = modelToAst(classes);
|
List<PreparedClass> clsNodes = modelToAst(classes);
|
||||||
|
prepareModules(classes);
|
||||||
if (controller.wasCancelled()) {
|
if (controller.wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -382,7 +397,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
controller.getUnprocessedClassSource(), classes,
|
controller.getUnprocessedClassSource(), classes,
|
||||||
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
||||||
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m),
|
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m),
|
||||||
controller.getClassInitializerInfo(), strict);
|
controller.getClassInitializerInfo(), strict
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public String importModule(String name) {
|
||||||
|
return JavaScriptTarget.this.importModule(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
renderingContext.setMinifying(obfuscated);
|
renderingContext.setMinifying(obfuscated);
|
||||||
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
|
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
|
||||||
controller.getDiagnostics(), renderingContext);
|
controller.getDiagnostics(), renderingContext);
|
||||||
|
@ -404,7 +425,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
renderer.setDebugEmitter(debugEmitter);
|
renderer.setDebugEmitter(debugEmitter);
|
||||||
}
|
}
|
||||||
renderer.getDebugEmitter().setLocationProvider(sourceWriter);
|
renderer.getDebugEmitter().setLocationProvider(sourceWriter);
|
||||||
for (Map.Entry<MethodReference, Injector> entry : methodInjectors.entrySet()) {
|
for (var entry : methodInjectors.entrySet()) {
|
||||||
renderingContext.addInjector(entry.getKey(), entry.getValue());
|
renderingContext.addInjector(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -462,21 +483,45 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private void printWrapperStart(SourceWriter writer) throws IOException {
|
private void printWrapperStart(SourceWriter writer) throws IOException {
|
||||||
writer.append("\"use strict\";").newLine();
|
writer.append("\"use strict\";").newLine();
|
||||||
printUmdStart(writer);
|
printUmdStart(writer);
|
||||||
writer.append("function($rt_globals,").ws().append("$rt_exports)").appendBlockStart();
|
writer.append("function($rt_globals,").ws().append("$rt_exports");
|
||||||
|
for (var moduleName : importedModules.values()) {
|
||||||
|
writer.append(",").ws().appendFunction(moduleName);
|
||||||
|
}
|
||||||
|
writer.append(")").appendBlockStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String importModule(String name) {
|
||||||
|
return importedModules.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printUmdStart(SourceWriter writer) throws IOException {
|
private void printUmdStart(SourceWriter writer) throws IOException {
|
||||||
writer.append("(function(root,").ws().append("module)").appendBlockStart();
|
writer.append("(function(root,").ws().append("module)").appendBlockStart();
|
||||||
writer.appendIf().append("typeof define").ws().append("===").ws().append("'function'")
|
writer.appendIf().append("typeof define").ws().append("===").ws().append("'function'")
|
||||||
.ws().append("&&").ws().append("define.amd)").appendBlockStart();
|
.ws().append("&&").ws().append("define.amd)").appendBlockStart();
|
||||||
writer.append("define(['exports'],").ws().append("function(exports)").ws().appendBlockStart();
|
writer.append("define(['exports'");
|
||||||
writer.append("module(root,").ws().append("exports);").softNewLine();
|
for (var moduleName : importedModules.keySet()) {
|
||||||
|
writer.append(',').ws().append('"').append(RenderingUtil.escapeString(moduleName)).append('"');
|
||||||
|
}
|
||||||
|
writer.append("],").ws().append("function(exports");
|
||||||
|
for (var moduleAlias : importedModules.values()) {
|
||||||
|
writer.append(',').ws().appendFunction(moduleAlias);
|
||||||
|
}
|
||||||
|
writer.append(")").ws().appendBlockStart();
|
||||||
|
writer.append("module(root,").ws().append("exports");
|
||||||
|
for (var moduleAlias : importedModules.values()) {
|
||||||
|
writer.append(',').ws().appendFunction(moduleAlias);
|
||||||
|
}
|
||||||
|
writer.append(");").softNewLine();
|
||||||
writer.outdent().append("});").softNewLine();
|
writer.outdent().append("});").softNewLine();
|
||||||
|
|
||||||
writer.appendElseIf().append("typeof exports").ws()
|
writer.appendElseIf().append("typeof exports").ws()
|
||||||
.append("===").ws().append("'object'").ws().append("&&").ws()
|
.append("===").ws().append("'object'").ws().append("&&").ws()
|
||||||
.append("typeof exports.nodeName").ws().append("!==").ws().append("'string')").appendBlockStart();
|
.append("typeof exports.nodeName").ws().append("!==").ws().append("'string')").appendBlockStart();
|
||||||
writer.append("module(global,").ws().append("exports);").softNewLine();
|
writer.append("module(global,").ws().append("exports");
|
||||||
|
for (var moduleName : importedModules.keySet()) {
|
||||||
|
writer.append(',').ws().append("require(\"").append(RenderingUtil.escapeString(moduleName)).append("\")");
|
||||||
|
}
|
||||||
|
writer.append(");").softNewLine();
|
||||||
|
|
||||||
writer.appendElse();
|
writer.appendElse();
|
||||||
writer.append("module(root,").ws().append("root);").softNewLine();
|
writer.append("module(root,").ws().append("root);").softNewLine();
|
||||||
|
@ -519,6 +564,28 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")";
|
return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void prepareModules(ListableClassHolderSource classes) {
|
||||||
|
var context = new ImporterContext(classes);
|
||||||
|
for (var className : classes.getClassNames()) {
|
||||||
|
var cls = classes.get(className);
|
||||||
|
for (var method : cls.getMethods()) {
|
||||||
|
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var providerContext = new ProviderContextImpl(method.getReference());
|
||||||
|
for (var provider : moduleImporterProviders) {
|
||||||
|
var importer = provider.apply(providerContext);
|
||||||
|
if (importer != null) {
|
||||||
|
context.method = method;
|
||||||
|
importer.importModules(context);
|
||||||
|
context.method = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<PreparedClass> modelToAst(ListableClassHolderSource classes) {
|
private List<PreparedClass> modelToAst(ListableClassHolderSource classes) {
|
||||||
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
|
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
|
||||||
controller.getDependencyInfo());
|
controller.getDependencyInfo());
|
||||||
|
@ -676,7 +743,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
|
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
ProviderContext context = new ProviderContextImpl(method.getReference());
|
ProviderContext context = new ProviderContextImpl(method.getReference());
|
||||||
for (Function<ProviderContext, Generator> provider : generatorProviders) {
|
for (var provider : generatorProviders) {
|
||||||
Generator generator = provider.apply(context);
|
Generator generator = provider.apply(context);
|
||||||
if (generator != null) {
|
if (generator != null) {
|
||||||
methodGenerators.put(method.getReference(), generator);
|
methodGenerators.put(method.getReference(), generator);
|
||||||
|
@ -684,7 +751,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Function<ProviderContext, Injector> provider : injectorProviders) {
|
for (var provider : injectorProviders) {
|
||||||
Injector injector = provider.apply(context);
|
Injector injector = provider.apply(context);
|
||||||
if (injector != null) {
|
if (injector != null) {
|
||||||
methodInjectors.put(method.getReference(), injector);
|
methodInjectors.put(method.getReference(), injector);
|
||||||
|
@ -756,6 +823,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
public ClassReaderSource getClassSource() {
|
public ClassReaderSource getClassSource() {
|
||||||
return controller.getUnprocessedClassSource();
|
return controller.getUnprocessedClassSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getService(Class<T> type) {
|
||||||
|
return controller.getServices().getService(type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PlatformMarker
|
@PlatformMarker
|
||||||
|
@ -820,4 +892,48 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
return classSource;
|
return classSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ImporterContext implements ModuleImporterContext {
|
||||||
|
private ListableClassReaderSource classSource;
|
||||||
|
MethodReader method;
|
||||||
|
|
||||||
|
ImporterContext(ListableClassReaderSource classSource) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodReader getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void importModule(String name) {
|
||||||
|
importedModules.computeIfAbsent(name, n -> "$rt_import_" + importedModules.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListableClassReaderSource getClassSource() {
|
||||||
|
return classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getClassLoader() {
|
||||||
|
return controller.getClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Properties getProperties() {
|
||||||
|
return JavaScriptTarget.this.controller.getProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DependencyInfo getDependency() {
|
||||||
|
return controller.getDependencyInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getService(Class<T> type) {
|
||||||
|
return controller.getServices().getService(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript;
|
package org.teavm.backend.javascript;
|
||||||
|
|
||||||
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public interface ProviderContext {
|
public interface ProviderContext extends ServiceRepository {
|
||||||
MethodReference getMethod();
|
MethodReference getMethod();
|
||||||
|
|
||||||
ClassReaderSource getClassSource();
|
ClassReaderSource getClassSource();
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.javascript;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
|
import org.teavm.backend.javascript.spi.ModuleImporter;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.vm.spi.RendererListener;
|
import org.teavm.vm.spi.RendererListener;
|
||||||
|
@ -32,6 +33,8 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension {
|
||||||
|
|
||||||
void addInjectorProvider(Function<ProviderContext, Injector> provider);
|
void addInjectorProvider(Function<ProviderContext, Injector> provider);
|
||||||
|
|
||||||
|
void addModuleImporterProvider(Function<ProviderContext, ModuleImporter> provider);
|
||||||
|
|
||||||
void add(RendererListener listener);
|
void add(RendererListener listener);
|
||||||
|
|
||||||
void addVirtualMethods(VirtualMethodContributor virtualMethods);
|
void addVirtualMethods(VirtualMethodContributor virtualMethods);
|
||||||
|
|
|
@ -1212,6 +1212,11 @@ public class Renderer implements RenderingManager {
|
||||||
public boolean isDynamicInitializer(String className) {
|
public boolean isDynamicInitializer(String className) {
|
||||||
return context.isDynamicInitializer(className);
|
return context.isDynamicInitializer(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String importModule(String name) {
|
||||||
|
return context.importModule(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
||||||
|
|
|
@ -47,7 +47,7 @@ import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
|
||||||
public class RenderingContext {
|
public abstract class RenderingContext {
|
||||||
private final DebugInformationEmitter debugEmitter;
|
private final DebugInformationEmitter debugEmitter;
|
||||||
private ClassReaderSource initialClassSource;
|
private ClassReaderSource initialClassSource;
|
||||||
private ListableClassReaderSource classSource;
|
private ListableClassReaderSource classSource;
|
||||||
|
@ -406,6 +406,8 @@ public class RenderingContext {
|
||||||
return strict;
|
return strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract String importModule(String name);
|
||||||
|
|
||||||
@PlatformMarker
|
@PlatformMarker
|
||||||
private static boolean isBootstrap() {
|
private static boolean isBootstrap() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1747,5 +1747,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
|
||||||
public ListableClassReaderSource getClassSource() {
|
public ListableClassReaderSource getClassSource() {
|
||||||
return context.getClassSource();
|
return context.getClassSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String importModule(String name) {
|
||||||
|
return context.importModule(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ import org.teavm.model.ValueType;
|
||||||
public interface GeneratorContext extends ServiceRepository {
|
public interface GeneratorContext extends ServiceRepository {
|
||||||
String getParameterName(int index);
|
String getParameterName(int index);
|
||||||
|
|
||||||
|
String importModule(String name);
|
||||||
|
|
||||||
ClassReaderSource getInitialClassSource();
|
ClassReaderSource getInitialClassSource();
|
||||||
|
|
||||||
ListableClassReaderSource getClassSource();
|
ListableClassReaderSource getClassSource();
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.teavm.model.ValueType;
|
||||||
public interface InjectorContext extends ServiceRepository {
|
public interface InjectorContext extends ServiceRepository {
|
||||||
Expr getArgument(int index);
|
Expr getArgument(int index);
|
||||||
|
|
||||||
|
String importModule(String name);
|
||||||
|
|
||||||
int argumentCount();
|
int argumentCount();
|
||||||
|
|
||||||
boolean isMinifying();
|
boolean isMinifying();
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.spi;
|
||||||
|
|
||||||
|
public interface ModuleImporter {
|
||||||
|
void importModules(ModuleImporterContext context);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.spi;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
import org.teavm.common.ServiceRepository;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
|
||||||
|
public interface ModuleImporterContext extends ServiceRepository {
|
||||||
|
MethodReader getMethod();
|
||||||
|
|
||||||
|
void importModule(String name);
|
||||||
|
|
||||||
|
ListableClassReaderSource getClassSource();
|
||||||
|
|
||||||
|
ClassLoader getClassLoader();
|
||||||
|
|
||||||
|
Properties getProperties();
|
||||||
|
|
||||||
|
DependencyInfo getDependency();
|
||||||
|
}
|
|
@ -106,7 +106,7 @@ public abstract class BaseTypeInference<T> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
type = doMerge(type, formerType);
|
type = doMerge(type, formerType);
|
||||||
if (Objects.equals(type, formerType)) {
|
if (Objects.equals(type, formerType) || type == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
types[variable] = type;
|
types[variable] = type;
|
||||||
|
|
|
@ -133,4 +133,6 @@ public @interface JSBody {
|
||||||
* <p>JavaScript code.</p>
|
* <p>JavaScript code.</p>
|
||||||
*/
|
*/
|
||||||
String script();
|
String script();
|
||||||
|
|
||||||
|
JSBodyImport[] imports() default {};
|
||||||
}
|
}
|
||||||
|
|
26
jso/core/src/main/java/org/teavm/jso/JSBodyImport.java
Normal file
26
jso/core/src/main/java/org/teavm/jso/JSBodyImport.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.jso;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface JSBodyImport {
|
||||||
|
String alias();
|
||||||
|
|
||||||
|
String fromModule();
|
||||||
|
}
|
27
jso/impl/src/main/java/org/teavm/jso/impl/Imports.java
Normal file
27
jso/impl/src/main/java/org/teavm/jso/impl/Imports.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.jso.impl;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@interface Imports {
|
||||||
|
String[] value();
|
||||||
|
}
|
|
@ -32,12 +32,15 @@ class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
private AstNode ast;
|
private AstNode ast;
|
||||||
private AstNode rootAst;
|
private AstNode rootAst;
|
||||||
private String[] parameterNames;
|
private String[] parameterNames;
|
||||||
|
private JsBodyImportInfo[] imports;
|
||||||
|
|
||||||
JSBodyAstEmitter(boolean isStatic, AstNode ast, AstNode rootAst, String[] parameterNames) {
|
JSBodyAstEmitter(boolean isStatic, AstNode ast, AstNode rootAst, String[] parameterNames,
|
||||||
|
JsBodyImportInfo[] imports) {
|
||||||
this.isStatic = isStatic;
|
this.isStatic = isStatic;
|
||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
this.rootAst = rootAst;
|
this.rootAst = rootAst;
|
||||||
this.parameterNames = parameterNames;
|
this.parameterNames = parameterNames;
|
||||||
|
this.imports = imports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,6 +57,10 @@ class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
astWriter.declareNameEmitter(parameterNames[i],
|
astWriter.declareNameEmitter(parameterNames[i],
|
||||||
prec -> context.writeExpr(context.getArgument(index), convert(prec)));
|
prec -> context.writeExpr(context.getArgument(index), convert(prec)));
|
||||||
}
|
}
|
||||||
|
for (var importInfo : imports) {
|
||||||
|
astWriter.declareNameEmitter(importInfo.alias,
|
||||||
|
prec -> context.getWriter().appendFunction(context.importModule(importInfo.fromModule)));
|
||||||
|
}
|
||||||
astWriter.hoist(rootAst);
|
astWriter.hoist(rootAst);
|
||||||
astWriter.print(ast, convert(context.getPrecedence()));
|
astWriter.print(ast, convert(context.getPrecedence()));
|
||||||
}
|
}
|
||||||
|
@ -148,9 +155,13 @@ class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
int index = paramIndex++;
|
int index = paramIndex++;
|
||||||
astWriter.declareNameEmitter("this", prec -> writer.append(context.getParameterName(index)));
|
astWriter.declareNameEmitter("this", prec -> writer.append(context.getParameterName(index)));
|
||||||
}
|
}
|
||||||
for (int i = 0; i < parameterNames.length; ++i) {
|
for (var parameterName : parameterNames) {
|
||||||
int index = paramIndex++;
|
int index = paramIndex++;
|
||||||
astWriter.declareNameEmitter(parameterNames[i], prec -> writer.append(context.getParameterName(index)));
|
astWriter.declareNameEmitter(parameterName, prec -> writer.append(context.getParameterName(index)));
|
||||||
|
}
|
||||||
|
for (var importInfo : imports) {
|
||||||
|
astWriter.declareNameEmitter(importInfo.alias,
|
||||||
|
prec -> writer.appendFunction(context.importModule(importInfo.fromModule)));
|
||||||
}
|
}
|
||||||
astWriter.hoist(rootAst);
|
astWriter.hoist(rootAst);
|
||||||
if (ast instanceof Block) {
|
if (ast instanceof Block) {
|
||||||
|
|
|
@ -26,22 +26,45 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
|
||||||
private MethodReference method;
|
private MethodReference method;
|
||||||
private String script;
|
private String script;
|
||||||
private String[] parameterNames;
|
private String[] parameterNames;
|
||||||
|
private JsBodyImportInfo[] imports;
|
||||||
|
|
||||||
public JSBodyBloatedEmitter(boolean isStatic, MethodReference method, String script, String[] parameterNames) {
|
JSBodyBloatedEmitter(boolean isStatic, MethodReference method, String script, String[] parameterNames,
|
||||||
|
JsBodyImportInfo[] imports) {
|
||||||
this.isStatic = isStatic;
|
this.isStatic = isStatic;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.script = script;
|
this.script = script;
|
||||||
this.parameterNames = parameterNames;
|
this.parameterNames = parameterNames;
|
||||||
|
this.imports = imports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(InjectorContext context) throws IOException {
|
public void emit(InjectorContext context) throws IOException {
|
||||||
emit(context.getWriter(), index -> context.writeExpr(context.getArgument(index)));
|
emit(context.getWriter(), new EmissionStrategy() {
|
||||||
|
@Override
|
||||||
|
public void emitArgument(int argument) {
|
||||||
|
context.writeExpr(context.getArgument(argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitModule(String name) throws IOException {
|
||||||
|
context.getWriter().append(context.importModule(name));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
emit(writer, index -> writer.append(context.getParameterName(index + 1)));
|
emit(writer, new EmissionStrategy() {
|
||||||
|
@Override
|
||||||
|
public void emitArgument(int argument) throws IOException {
|
||||||
|
writer.append(context.getParameterName(argument + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitModule(String name) throws IOException {
|
||||||
|
writer.append(context.importModule(name));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emit(SourceWriter writer, EmissionStrategy strategy) throws IOException {
|
private void emit(SourceWriter writer, EmissionStrategy strategy) throws IOException {
|
||||||
|
@ -50,22 +73,43 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
|
||||||
writer.append("if (!").appendMethodBody(method).append(".$native)").ws().append('{').indent().newLine();
|
writer.append("if (!").appendMethodBody(method).append(".$native)").ws().append('{').indent().newLine();
|
||||||
writer.appendMethodBody(method).append(".$native").ws().append('=').ws().append("function(");
|
writer.appendMethodBody(method).append(".$native").ws().append('=').ws().append("function(");
|
||||||
int count = method.parameterCount();
|
int count = method.parameterCount();
|
||||||
|
|
||||||
|
var first = true;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
if (i > 0) {
|
if (!first) {
|
||||||
writer.append(',').ws();
|
writer.append(',').ws();
|
||||||
}
|
}
|
||||||
|
first = false;
|
||||||
writer.append('_').append(i);
|
writer.append('_').append(i);
|
||||||
}
|
}
|
||||||
|
for (var i = 0; i < imports.length; ++i) {
|
||||||
|
if (!first) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
writer.append("_i").append(i);
|
||||||
|
}
|
||||||
writer.append(')').ws().append('{').softNewLine().indent();
|
writer.append(')').ws().append('{').softNewLine().indent();
|
||||||
|
|
||||||
writer.append("return (function(");
|
writer.append("return (function(");
|
||||||
|
|
||||||
|
first = true;
|
||||||
for (int i = 0; i < bodyParamCount; ++i) {
|
for (int i = 0; i < bodyParamCount; ++i) {
|
||||||
if (i > 0) {
|
if (!first) {
|
||||||
writer.append(',').ws();
|
writer.append(',').ws();
|
||||||
}
|
}
|
||||||
|
first = false;
|
||||||
String name = parameterNames[i];
|
String name = parameterNames[i];
|
||||||
writer.append(name);
|
writer.append(name);
|
||||||
}
|
}
|
||||||
|
for (var importInfo : imports) {
|
||||||
|
if (!first) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
writer.append(importInfo.alias);
|
||||||
|
}
|
||||||
|
|
||||||
writer.append(')').ws().append('{').softNewLine().indent();
|
writer.append(')').ws().append('{').softNewLine().indent();
|
||||||
writer.append(script).softNewLine();
|
writer.append(script).softNewLine();
|
||||||
writer.outdent().append("})");
|
writer.outdent().append("})");
|
||||||
|
@ -73,12 +117,23 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
|
||||||
writer.append(".call");
|
writer.append(".call");
|
||||||
}
|
}
|
||||||
writer.append('(');
|
writer.append('(');
|
||||||
|
|
||||||
|
first = true;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
if (i > 0) {
|
if (!first) {
|
||||||
writer.append(',').ws();
|
writer.append(',').ws();
|
||||||
}
|
}
|
||||||
|
first = false;
|
||||||
writer.append('_').append(i);
|
writer.append('_').append(i);
|
||||||
}
|
}
|
||||||
|
for (var i = 0; i < imports.length; ++i) {
|
||||||
|
if (!first) {
|
||||||
|
writer.append(',').ws();
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
writer.append("_i").append(i);
|
||||||
|
}
|
||||||
|
|
||||||
writer.append(");").softNewLine();
|
writer.append(");").softNewLine();
|
||||||
writer.outdent().append("};").softNewLine();
|
writer.outdent().append("};").softNewLine();
|
||||||
writer.appendMethodBody(method).ws().append('=').ws().appendMethodBody(method).append(".$native;")
|
writer.appendMethodBody(method).ws().append('=').ws().appendMethodBody(method).append(".$native;")
|
||||||
|
@ -97,5 +152,7 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
|
||||||
|
|
||||||
interface EmissionStrategy {
|
interface EmissionStrategy {
|
||||||
void emitArgument(int argument) throws IOException;
|
void emitArgument(int argument) throws IOException;
|
||||||
|
|
||||||
|
void emitModule(String name) throws IOException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,11 @@ import java.util.Set;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
class JSBodyRepository {
|
class JSBodyRepository {
|
||||||
public final Map<MethodReference, JSBodyEmitter> emitters = new HashMap<>();
|
final Map<MethodReference, JSBodyEmitter> emitters = new HashMap<>();
|
||||||
public final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
|
final Map<MethodReference, JsBodyImportInfo[]> imports = new HashMap<>();
|
||||||
public final Set<MethodReference> processedMethods = new HashSet<>();
|
final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
|
||||||
public final Set<MethodReference> inlineMethods = new HashSet<>();
|
final Set<MethodReference> processedMethods = new HashSet<>();
|
||||||
public final Map<MethodReference, MethodReference> callbackCallees = new HashMap<>();
|
final Set<MethodReference> inlineMethods = new HashSet<>();
|
||||||
public final Map<MethodReference, Set<MethodReference>> callbackMethods = new HashMap<>();
|
final Map<MethodReference, MethodReference> callbackCallees = new HashMap<>();
|
||||||
|
final Map<MethodReference, Set<MethodReference>> callbackMethods = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -780,10 +780,24 @@ class JSClassProcessor {
|
||||||
}
|
}
|
||||||
var body = ((FunctionNode) rootNode.getFirstChild()).getBody();
|
var body = ((FunctionNode) rootNode.getFirstChild()).getBody();
|
||||||
|
|
||||||
|
JsBodyImportInfo[] imports;
|
||||||
|
var importsValue = bodyAnnot.getValue("imports");
|
||||||
|
if (importsValue != null) {
|
||||||
|
var importsList = importsValue.getList();
|
||||||
|
imports = new JsBodyImportInfo[importsList.size()];
|
||||||
|
for (var i = 0; i < importsList.size(); ++i) {
|
||||||
|
var importAnnot = importsList.get(0).getAnnotation();
|
||||||
|
imports[i] = new JsBodyImportInfo(importAnnot.getValue("alias").getString(),
|
||||||
|
importAnnot.getValue("fromModule").getString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imports = new JsBodyImportInfo[0];
|
||||||
|
}
|
||||||
|
|
||||||
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
||||||
if (errorReporter.hasErrors()) {
|
if (errorReporter.hasErrors()) {
|
||||||
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
||||||
script, parameterNames));
|
script, parameterNames, imports));
|
||||||
} else {
|
} else {
|
||||||
var expr = JSBodyInlineUtil.isSuitableForInlining(methodToProcess.getReference(),
|
var expr = JSBodyInlineUtil.isSuitableForInlining(methodToProcess.getReference(),
|
||||||
parameterNames, body);
|
parameterNames, body);
|
||||||
|
@ -793,7 +807,11 @@ class JSClassProcessor {
|
||||||
expr = body;
|
expr = body;
|
||||||
}
|
}
|
||||||
javaInvocationProcessor.process(location, expr);
|
javaInvocationProcessor.process(location, expr);
|
||||||
repository.emitters.put(proxyMethod, new JSBodyAstEmitter(isStatic, expr, rootNode, parameterNames));
|
var emitter = new JSBodyAstEmitter(isStatic, expr, rootNode, parameterNames, imports);
|
||||||
|
repository.emitters.put(proxyMethod, emitter);
|
||||||
|
}
|
||||||
|
if (imports.length > 0) {
|
||||||
|
repository.imports.put(proxyMethod, imports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,5 +78,10 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||||
wrapperGenerator);
|
wrapperGenerator);
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, JS.class);
|
TeaVMPluginUtil.handleNatives(host, JS.class);
|
||||||
|
|
||||||
|
jsHost.addModuleImporterProvider(providerContext -> {
|
||||||
|
var imports = repository.imports.get(providerContext.getMethod());
|
||||||
|
return imports != null ? new JsBodyImportsContributor(imports) : null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.jso.impl;
|
||||||
|
|
||||||
|
class JsBodyImportInfo {
|
||||||
|
final String alias;
|
||||||
|
final String fromModule;
|
||||||
|
|
||||||
|
JsBodyImportInfo(String alias, String fromModule) {
|
||||||
|
this.alias = alias;
|
||||||
|
this.fromModule = fromModule;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.jso.impl;
|
||||||
|
|
||||||
|
import org.teavm.backend.javascript.spi.ModuleImporter;
|
||||||
|
import org.teavm.backend.javascript.spi.ModuleImporterContext;
|
||||||
|
|
||||||
|
class JsBodyImportsContributor implements ModuleImporter {
|
||||||
|
private JsBodyImportInfo[] imports;
|
||||||
|
|
||||||
|
JsBodyImportsContributor(JsBodyImportInfo[] imports) {
|
||||||
|
this.imports = imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void importModules(ModuleImporterContext context) {
|
||||||
|
for (var importInfo : imports) {
|
||||||
|
context.importModule(importInfo.fromModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java
Normal file
52
tests/src/test/java/org/teavm/jso/test/ImportModuleTest.java
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.jso.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSBodyImport;
|
||||||
|
import org.teavm.junit.AttachJavaScript;
|
||||||
|
import org.teavm.junit.SkipJVM;
|
||||||
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.WholeClassCompilation;
|
||||||
|
|
||||||
|
@RunWith(TeaVMTestRunner.class)
|
||||||
|
@SkipJVM
|
||||||
|
@WholeClassCompilation
|
||||||
|
public class ImportModuleTest {
|
||||||
|
@Test
|
||||||
|
@AttachJavaScript({
|
||||||
|
"org/teavm/jso/test/amd.js",
|
||||||
|
"org/teavm/jso/test/amdModule.js"
|
||||||
|
})
|
||||||
|
public void amd() {
|
||||||
|
assertEquals(23, runTestFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@AttachJavaScript("org/teavm/jso/test/commonjs.js")
|
||||||
|
public void commonjs() {
|
||||||
|
assertEquals(23, runTestFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(
|
||||||
|
script = "return testModule.foo();",
|
||||||
|
imports = @JSBodyImport(alias = "testModule", fromModule = "testModule.js")
|
||||||
|
)
|
||||||
|
private static native int runTestFunction();
|
||||||
|
}
|
47
tests/src/test/resources/org/teavm/jso/test/amd.js
Normal file
47
tests/src/test/resources/org/teavm/jso/test/amd.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let main;
|
||||||
|
const define = (function() {
|
||||||
|
const modules = new Map();
|
||||||
|
function def() {
|
||||||
|
let index = 0;
|
||||||
|
const moduleName = typeof arguments[index] === 'string' ? arguments[index++] : null;
|
||||||
|
const deps = arguments[index++];
|
||||||
|
const module = arguments[index++];
|
||||||
|
|
||||||
|
const exports = Object.create(null);
|
||||||
|
const args = [];
|
||||||
|
for (const dep of deps) {
|
||||||
|
if (dep === 'exports') {
|
||||||
|
args.push(exports);
|
||||||
|
} else {
|
||||||
|
args.push(modules[dep]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = module.apply(this, args);
|
||||||
|
if (typeof result === 'undefined') {
|
||||||
|
result = exports;
|
||||||
|
}
|
||||||
|
if (moduleName !== null) {
|
||||||
|
modules[moduleName] = result;
|
||||||
|
} else {
|
||||||
|
main = result.main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def.amd = {};
|
||||||
|
return def;
|
||||||
|
})();
|
23
tests/src/test/resources/org/teavm/jso/test/amdModule.js
Normal file
23
tests/src/test/resources/org/teavm/jso/test/amdModule.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define("testModule.js", [], () => {
|
||||||
|
return {
|
||||||
|
foo() {
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
36
tests/src/test/resources/org/teavm/jso/test/commonjs.js
Normal file
36
tests/src/test/resources/org/teavm/jso/test/commonjs.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function require(name) {
|
||||||
|
switch (name) {
|
||||||
|
case "testModule.js": {
|
||||||
|
return {
|
||||||
|
foo() {
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown module: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let global = this;
|
||||||
|
let exports = {};
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
exports.main.apply(this, arguments);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.junit;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface AttachJavaScript {
|
||||||
|
String[] value();
|
||||||
|
}
|
|
@ -25,10 +25,11 @@ import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -66,7 +67,7 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
private ConcurrentMap<Integer, CallbackWrapper> awaitingRuns = new ConcurrentHashMap<>();
|
private ConcurrentMap<Integer, CallbackWrapper> awaitingRuns = new ConcurrentHashMap<>();
|
||||||
private ObjectMapper objectMapper = new ObjectMapper();
|
private ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
public BrowserRunStrategy(File baseDir, String type, Function<String, Process> browserRunner) {
|
BrowserRunStrategy(File baseDir, String type, Function<String, Process> browserRunner) {
|
||||||
this.baseDir = baseDir;
|
this.baseDir = baseDir;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.browserRunner = browserRunner;
|
this.browserRunner = browserRunner;
|
||||||
|
@ -186,6 +187,16 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
testNode.set("type", nf.textNode(type));
|
testNode.set("type", nf.textNode(type));
|
||||||
testNode.set("name", nf.textNode(run.getFileName()));
|
testNode.set("name", nf.textNode(run.getFileName()));
|
||||||
testNode.set("file", nf.textNode("tests/" + relPath));
|
testNode.set("file", nf.textNode("tests/" + relPath));
|
||||||
|
|
||||||
|
var additionalJs = additionalJs(run);
|
||||||
|
if (additionalJs.length > 0) {
|
||||||
|
var additionalJsJson = nf.arrayNode();
|
||||||
|
for (var additionalFile : additionalJs) {
|
||||||
|
additionalJsJson.add("resources/" + additionalFile);
|
||||||
|
}
|
||||||
|
testNode.set("additionalFiles", additionalJsJson);
|
||||||
|
}
|
||||||
|
|
||||||
if (run.getArgument() != null) {
|
if (run.getArgument() != null) {
|
||||||
testNode.set("argument", nf.textNode(run.getArgument()));
|
testNode.set("argument", nf.textNode(run.getArgument()));
|
||||||
}
|
}
|
||||||
|
@ -207,6 +218,27 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
return !callbackWrapper.shouldRepeat;
|
return !callbackWrapper.shouldRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String[] additionalJs(TestRun run) {
|
||||||
|
var result = new LinkedHashSet<String>();
|
||||||
|
|
||||||
|
var method = run.getMethod();
|
||||||
|
var attachAnnot = method.getAnnotation(AttachJavaScript.class);
|
||||||
|
if (attachAnnot != null) {
|
||||||
|
result.addAll(List.of(attachAnnot.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cls = method.getDeclaringClass();
|
||||||
|
while (cls != null) {
|
||||||
|
var classAttachAnnot = cls.getAnnotation(AttachJavaScript.class);
|
||||||
|
if (classAttachAnnot != null) {
|
||||||
|
result.addAll(List.of(attachAnnot.value()));
|
||||||
|
}
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
class TestCodeServlet extends HttpServlet {
|
class TestCodeServlet extends HttpServlet {
|
||||||
private WebSocketServletFactory wsFactory;
|
private WebSocketServletFactory wsFactory;
|
||||||
private Map<String, String> contentCache = new ConcurrentHashMap<>();
|
private Map<String, String> contentCache = new ConcurrentHashMap<>();
|
||||||
|
@ -225,7 +257,7 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
String path = req.getRequestURI();
|
String path = req.getRequestURI();
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
if (!path.startsWith("/")) {
|
if (!path.startsWith("/")) {
|
||||||
|
@ -270,7 +302,20 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
resp.setContentType("application/wasm");
|
resp.setContentType("application/wasm");
|
||||||
}
|
}
|
||||||
try (FileInputStream input = new FileInputStream(file)) {
|
try (FileInputStream input = new FileInputStream(file)) {
|
||||||
copy(input, resp.getOutputStream());
|
input.transferTo(resp.getOutputStream());
|
||||||
|
}
|
||||||
|
resp.getOutputStream().flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path.startsWith("/resources/")) {
|
||||||
|
var relPath = path.substring("/resources/".length());
|
||||||
|
var classLoader = BrowserRunStrategy.class.getClassLoader();
|
||||||
|
try (var input = classLoader.getResourceAsStream(relPath)) {
|
||||||
|
if (input != null) {
|
||||||
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
input.transferTo(resp.getOutputStream());
|
||||||
|
} else {
|
||||||
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
}
|
}
|
||||||
resp.getOutputStream().flush();
|
resp.getOutputStream().flush();
|
||||||
}
|
}
|
||||||
|
@ -309,17 +354,6 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(InputStream input, OutputStream output) throws IOException {
|
|
||||||
byte[] buffer = new byte[2048];
|
|
||||||
while (true) {
|
|
||||||
int bytes = input.read(buffer);
|
|
||||||
if (bytes < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
output.write(buffer, 0, bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestCodeSocket extends WebSocketAdapter {
|
class TestCodeSocket extends WebSocketAdapter {
|
||||||
|
|
|
@ -20,7 +20,8 @@ window.addEventListener("message", event => {
|
||||||
let request = event.data;
|
let request = event.data;
|
||||||
switch (request.type) {
|
switch (request.type) {
|
||||||
case "JAVASCRIPT":
|
case "JAVASCRIPT":
|
||||||
appendFiles([request.file], 0, () => {
|
const files = request.additionalFiles ? [...request.additionalFiles, request.file] : [request.file];
|
||||||
|
appendFiles(files, 0, () => {
|
||||||
launchTest(request.argument, response => {
|
launchTest(request.argument, response => {
|
||||||
event.source.postMessage(response, "*");
|
event.source.postMessage(response, "*");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user