mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: support exporting declarations as JS entities from module
This commit is contained in:
parent
73dda91d35
commit
0dcc25d66b
|
@ -25,7 +25,9 @@ import org.teavm.interop.NoSideEffects;
|
|||
import org.teavm.interop.Platforms;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.UnsupportedOn;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.core.JSDate;
|
||||
import org.teavm.jso.impl.JS;
|
||||
|
||||
public class TDate implements TComparable<TDate> {
|
||||
private long value;
|
||||
|
@ -420,7 +422,7 @@ public class TDate implements TComparable<TDate> {
|
|||
} else if (PlatformDetector.isWebAssembly()) {
|
||||
return toStringWebAssembly(value);
|
||||
} else if (PlatformDetector.isWebAssemblyGC()) {
|
||||
return toStringWebAssemblyGC(value);
|
||||
return JS.unwrapString(toStringWebAssemblyGC(value));
|
||||
} else {
|
||||
return JSDate.create(value).stringValue();
|
||||
}
|
||||
|
@ -435,7 +437,7 @@ public class TDate implements TComparable<TDate> {
|
|||
private static native String toStringWebAssembly(double date);
|
||||
|
||||
@Import(module = "teavmDate", name = "dateToString")
|
||||
private static native String toStringWebAssemblyGC(double date);
|
||||
private static native JSObject toStringWebAssemblyGC(double date);
|
||||
|
||||
@Deprecated
|
||||
public String toLocaleString() {
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.teavm.backend.wasm;
|
|||
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
|
@ -34,47 +33,6 @@ public class WasmGCModuleGenerator {
|
|||
createInitializer();
|
||||
}
|
||||
|
||||
|
||||
public WasmFunction generateMainFunction(String entryPoint) {
|
||||
return declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint,
|
||||
"main", ValueType.parse(String[].class), ValueType.VOID));
|
||||
}
|
||||
|
||||
public WasmFunction generateCreateStringBuilderFunction() {
|
||||
return declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "createStringBuilder", StringBuilder.class));
|
||||
}
|
||||
|
||||
public WasmFunction generateCreateStringArrayFunction() {
|
||||
return declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "createStringArray", int.class, String[].class));
|
||||
}
|
||||
|
||||
public WasmFunction generateAppendCharFunction() {
|
||||
return declarationsGenerator.functions().forInstanceMethod(new MethodReference(
|
||||
StringBuilder.class, "append", char.class, StringBuilder.class));
|
||||
}
|
||||
|
||||
public WasmFunction generateBuildStringFunction() {
|
||||
return declarationsGenerator.functions().forInstanceMethod(new MethodReference(
|
||||
StringBuilder.class, "toString", String.class));
|
||||
}
|
||||
|
||||
public WasmFunction generateSetToStringArrayFunction() {
|
||||
return declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "setToStringArray", String[].class, int.class, String.class, void.class));
|
||||
}
|
||||
|
||||
public WasmFunction generateStringLengthFunction() {
|
||||
return declarationsGenerator.functions().forInstanceMethod(new MethodReference(
|
||||
String.class, "length", int.class));
|
||||
}
|
||||
|
||||
public WasmFunction generateCharAtFunction() {
|
||||
return declarationsGenerator.functions().forInstanceMethod(new MethodReference(
|
||||
String.class, "charAt", int.class, char.class));
|
||||
}
|
||||
|
||||
public WasmFunction generateReportGarbageCollectedStringFunction() {
|
||||
var entryType = ValueType.object(StringInternPool.class.getName() + "$Entry");
|
||||
return declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
|
|
|
@ -21,20 +21,28 @@ import java.util.Comparator;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import org.teavm.backend.wasm.debug.DebugLines;
|
||||
import org.teavm.backend.wasm.debug.ExternalDebugFile;
|
||||
import org.teavm.backend.wasm.debug.GCDebugInfoBuilder;
|
||||
import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
|
||||
import org.teavm.backend.wasm.gc.WasmGCClassConsumer;
|
||||
import org.teavm.backend.wasm.gc.WasmGCClassConsumerContext;
|
||||
import org.teavm.backend.wasm.gc.WasmGCDependencies;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactory;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerators;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsics;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.backend.wasm.optimization.WasmUsageCounter;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
|
||||
|
@ -43,10 +51,12 @@ import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
|||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation;
|
||||
import org.teavm.backend.wasm.transformation.gc.ClassLoaderResourceTransformation;
|
||||
import org.teavm.backend.wasm.transformation.gc.EntryPointTransformation;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.interop.Platforms;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ListableClassHolderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -73,6 +83,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
private List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories = new ArrayList<>();
|
||||
private Map<MethodReference, WasmGCCustomGenerator> customCustomGenerators = new HashMap<>();
|
||||
private List<WasmGCCustomGeneratorFactory> customGeneratorFactories = new ArrayList<>();
|
||||
private EntryPointTransformation entryPointTransformation = new EntryPointTransformation();
|
||||
private List<WasmGCClassConsumer> classConsumers = new ArrayList<>();
|
||||
|
||||
public void setObfuscated(boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
|
@ -115,11 +127,22 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
customTypeMapperFactories.add(customTypeMapperFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClassConsumer(WasmGCClassConsumer consumer) {
|
||||
classConsumers.add(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setController(TeaVMTargetController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntryPoint(String entryPoint, String name) {
|
||||
entryPointTransformation.setEntryPoint(entryPoint);
|
||||
entryPointTransformation.setEntryPointName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VariableCategoryProvider variableCategoryProvider() {
|
||||
return null;
|
||||
|
@ -134,7 +157,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
public List<ClassHolderTransformer> getTransformers() {
|
||||
return List.of(
|
||||
new BaseClassesTransformation(),
|
||||
new ClassLoaderResourceTransformation()
|
||||
new ClassLoaderResourceTransformation(),
|
||||
entryPointTransformation
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -181,6 +205,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
@Override
|
||||
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
|
||||
var module = new WasmModule();
|
||||
module.memoryExportName = "teavm.memory";
|
||||
var customGenerators = new WasmGCCustomGenerators(classes, controller.getServices(),
|
||||
customGeneratorFactories, customCustomGenerators,
|
||||
controller.getProperties());
|
||||
|
@ -198,47 +223,30 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
intrinsics,
|
||||
customTypeMapperFactories,
|
||||
controller::isVirtual,
|
||||
strict
|
||||
strict,
|
||||
controller.getEntryPoint()
|
||||
);
|
||||
declarationsGenerator.setFriendlyToDebugger(controller.isFriendlyToDebugger());
|
||||
var moduleGenerator = new WasmGCModuleGenerator(declarationsGenerator);
|
||||
|
||||
var mainFunction = moduleGenerator.generateMainFunction(controller.getEntryPoint());
|
||||
mainFunction.setExportName(controller.getEntryPointName());
|
||||
mainFunction.setName(controller.getEntryPointName());
|
||||
|
||||
var stringBuilderFunction = moduleGenerator.generateCreateStringBuilderFunction();
|
||||
stringBuilderFunction.setExportName("createStringBuilder");
|
||||
|
||||
var createStringArrayFunction = moduleGenerator.generateCreateStringArrayFunction();
|
||||
createStringArrayFunction.setExportName("createStringArray");
|
||||
|
||||
var appendCharFunction = moduleGenerator.generateAppendCharFunction();
|
||||
appendCharFunction.setExportName("appendChar");
|
||||
|
||||
var buildStringFunction = moduleGenerator.generateBuildStringFunction();
|
||||
buildStringFunction.setExportName("buildString");
|
||||
|
||||
var setArrayFunction = moduleGenerator.generateSetToStringArrayFunction();
|
||||
setArrayFunction.setExportName("setToStringArray");
|
||||
|
||||
var stringLengthFunction = moduleGenerator.generateStringLengthFunction();
|
||||
stringLengthFunction.setExportName("stringLength");
|
||||
|
||||
var charAtFunction = moduleGenerator.generateCharAtFunction();
|
||||
charAtFunction.setExportName("charAt");
|
||||
var classConsumerContext = createClassConsumerContext(classes, declarationsGenerator);
|
||||
for (var cls : classes.getClassNames()) {
|
||||
for (var consumer : classConsumers) {
|
||||
consumer.accept(classConsumerContext, cls);
|
||||
}
|
||||
}
|
||||
|
||||
var internMethod = controller.getDependencyInfo().getMethod(new MethodReference(String.class,
|
||||
"intern", String.class));
|
||||
if (internMethod != null && internMethod.isUsed()) {
|
||||
var removeStringEntryFunction = moduleGenerator.generateReportGarbageCollectedStringFunction();
|
||||
removeStringEntryFunction.setExportName("reportGarbageCollectedString");
|
||||
removeStringEntryFunction.setExportName("teavm.reportGarbageCollectedString");
|
||||
}
|
||||
|
||||
var exceptionMessageRef = new MethodReference(Throwable.class, "getMessage", Throwable.class);
|
||||
if (controller.getDependencyInfo().getMethod(exceptionMessageRef) != null) {
|
||||
var exceptionMessageFunction = declarationsGenerator.functions().forInstanceMethod(exceptionMessageRef);
|
||||
exceptionMessageFunction.setExportName("exceptionMessage");
|
||||
exceptionMessageFunction.setExportName("teavm.exceptionMessage");
|
||||
}
|
||||
|
||||
moduleGenerator.generate();
|
||||
|
@ -248,6 +256,63 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
emitWasmFile(module, buildTarget, outputName, debugInfoBuilder);
|
||||
}
|
||||
|
||||
private WasmGCClassConsumerContext createClassConsumerContext(
|
||||
ClassReaderSource classes,
|
||||
WasmGCDeclarationsGenerator generator
|
||||
) {
|
||||
return new WasmGCClassConsumerContext() {
|
||||
@Override
|
||||
public ClassReaderSource classes() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmModule module() {
|
||||
return generator.module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmFunctionTypes functionTypes() {
|
||||
return generator.functionTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseWasmFunctionRepository functions() {
|
||||
return generator.functions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCNameProvider names() {
|
||||
return generator.names();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCStringProvider strings() {
|
||||
return generator.strings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCTypeMapper typeMapper() {
|
||||
return generator.typeMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmTag exceptionTag() {
|
||||
return generator.exceptionTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return controller.getEntryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
generator.addToInitializer(initializerContributor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void adjustModuleMemory(WasmModule module) {
|
||||
var memorySize = 0;
|
||||
for (var segment : module.getSegments()) {
|
||||
|
|
|
@ -33,4 +33,6 @@ public interface TeaVMWasmGCHost extends TeaVMHostExtension {
|
|||
void addGenerator(MethodReference method, WasmGCCustomGenerator generator);
|
||||
|
||||
void addCustomTypeMapperFactory(WasmGCCustomTypeMapperFactory customTypeMapperFactory);
|
||||
|
||||
void addClassConsumer(WasmGCClassConsumer consumer);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2024 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.wasm.gc;
|
||||
|
||||
public interface WasmGCClassConsumer {
|
||||
void accept(WasmGCClassConsumerContext context, String className);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 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.wasm.gc;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
|
||||
public interface WasmGCClassConsumerContext {
|
||||
ClassReaderSource classes();
|
||||
|
||||
WasmModule module();
|
||||
|
||||
WasmFunctionTypes functionTypes();
|
||||
|
||||
BaseWasmFunctionRepository functions();
|
||||
|
||||
WasmGCNameProvider names();
|
||||
|
||||
WasmGCStringProvider strings();
|
||||
|
||||
WasmGCTypeMapper typeMapper();
|
||||
|
||||
WasmTag exceptionTag();
|
||||
|
||||
String entryPoint();
|
||||
|
||||
void addToInitializer(Consumer<WasmFunction> initializerContributor);
|
||||
}
|
|
@ -17,6 +17,7 @@ package org.teavm.backend.wasm.generate.gc;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||
|
@ -29,8 +30,10 @@ import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
|||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCMethodGenerator;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
|
@ -62,7 +65,8 @@ public class WasmGCDeclarationsGenerator {
|
|||
WasmGCIntrinsicProvider intrinsics,
|
||||
List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories,
|
||||
Predicate<MethodReference> isVirtual,
|
||||
boolean strict
|
||||
boolean strict,
|
||||
String entryPoint
|
||||
) {
|
||||
this.module = module;
|
||||
hierarchy = new ClassHierarchy(classes);
|
||||
|
@ -82,6 +86,7 @@ public class WasmGCDeclarationsGenerator {
|
|||
customGenerators,
|
||||
intrinsics,
|
||||
strict,
|
||||
entryPoint,
|
||||
initializerContributors::add
|
||||
);
|
||||
var tags = new TagRegistry(classes, hierarchy);
|
||||
|
@ -157,4 +162,20 @@ public class WasmGCDeclarationsGenerator {
|
|||
public WasmFunction dummyInitializer() {
|
||||
return methodGenerator.getDummyInitializer();
|
||||
}
|
||||
|
||||
public WasmGCNameProvider names() {
|
||||
return methodGenerator.names;
|
||||
}
|
||||
|
||||
public WasmGCStringProvider strings() {
|
||||
return classGenerator.strings;
|
||||
}
|
||||
|
||||
public WasmTag exceptionTag() {
|
||||
return methodGenerator.getGenerationContext().getExceptionTag();
|
||||
}
|
||||
|
||||
public void addToInitializer(Consumer<WasmFunction> contributor) {
|
||||
methodGenerator.getGenerationContext().addToInitializer(contributor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
private Map<String, Set<String>> interfaceImplementors;
|
||||
private WasmGCNameProvider names;
|
||||
private boolean strict;
|
||||
private String entryPoint;
|
||||
private Consumer<WasmGCInitializerContributor> initializerContributors;
|
||||
|
||||
public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
|
||||
|
@ -73,7 +74,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider,
|
||||
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings,
|
||||
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics,
|
||||
WasmGCNameProvider names, boolean strict, Consumer<WasmGCInitializerContributor> initializerContributors) {
|
||||
WasmGCNameProvider names, boolean strict, String entryPoint,
|
||||
Consumer<WasmGCInitializerContributor> initializerContributors) {
|
||||
this.module = module;
|
||||
this.virtualTables = virtualTables;
|
||||
this.typeMapper = typeMapper;
|
||||
|
@ -90,6 +92,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
this.intrinsics = intrinsics;
|
||||
this.names = names;
|
||||
this.strict = strict;
|
||||
this.entryPoint = entryPoint;
|
||||
this.initializerContributors = initializerContributors;
|
||||
}
|
||||
|
||||
|
@ -109,6 +112,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
return strings;
|
||||
}
|
||||
|
||||
public String entryPoint() {
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
public WasmGCVirtualTableProvider virtualTables() {
|
||||
return virtualTables;
|
||||
}
|
||||
|
@ -136,7 +143,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
if (exceptionTag == null) {
|
||||
exceptionTag = new WasmTag(functionTypes.of(null,
|
||||
classInfoProvider.getClassInfo("java.lang.Throwable").getStructure().getReference()));
|
||||
exceptionTag.setExportName("javaException");
|
||||
exceptionTag.setExportName("teavm.javaException");
|
||||
module.tags.add(exceptionTag);
|
||||
}
|
||||
return exceptionTag;
|
||||
|
|
|
@ -844,6 +844,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
return context.getExceptionTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return context.entryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
|
|
|
@ -72,7 +72,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
private ClassInitializerInfo classInitInfo;
|
||||
private WasmFunctionTypes functionTypes;
|
||||
private WasmGCSupertypeFunctionProvider supertypeFunctions;
|
||||
private WasmGCNameProvider names;
|
||||
public final WasmGCNameProvider names;
|
||||
private Diagnostics diagnostics;
|
||||
private WasmGCTypeMapper typeMapper;
|
||||
private WasmGCCustomGeneratorProvider customGenerators;
|
||||
|
@ -88,6 +88,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
private WasmGCStandardClasses standardClasses;
|
||||
private WasmGCStringProvider strings;
|
||||
private boolean strict;
|
||||
private String entryPoint;
|
||||
private Consumer<WasmGCInitializerContributor> initializerContributors;
|
||||
|
||||
public WasmGCMethodGenerator(
|
||||
|
@ -103,6 +104,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
WasmGCCustomGeneratorProvider customGenerators,
|
||||
WasmGCIntrinsicProvider intrinsics,
|
||||
boolean strict,
|
||||
String entryPoint,
|
||||
Consumer<WasmGCInitializerContributor> initializerContributors
|
||||
) {
|
||||
this.module = module;
|
||||
|
@ -117,6 +119,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
this.customGenerators = customGenerators;
|
||||
this.intrinsics = intrinsics;
|
||||
this.strict = strict;
|
||||
this.entryPoint = entryPoint;
|
||||
this.initializerContributors = initializerContributors;
|
||||
}
|
||||
|
||||
|
@ -343,7 +346,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
return decompiler;
|
||||
}
|
||||
|
||||
private WasmGCGenerationContext getGenerationContext() {
|
||||
public WasmGCGenerationContext getGenerationContext() {
|
||||
if (context == null) {
|
||||
context = new WasmGCGenerationContext(
|
||||
module,
|
||||
|
@ -362,6 +365,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
intrinsics,
|
||||
names,
|
||||
strict,
|
||||
entryPoint,
|
||||
initializerContributors
|
||||
);
|
||||
}
|
||||
|
@ -434,6 +438,11 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
return context.strings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return context.entryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
|
|
|
@ -51,5 +51,7 @@ public interface WasmGCCustomGeneratorContext {
|
|||
|
||||
WasmGCStringProvider strings();
|
||||
|
||||
String entryPoint();
|
||||
|
||||
void addToInitializer(Consumer<WasmFunction> initializerContributor);
|
||||
}
|
||||
|
|
|
@ -61,5 +61,7 @@ public interface WasmGCIntrinsicContext {
|
|||
|
||||
WasmTag exceptionTag();
|
||||
|
||||
String entryPoint();
|
||||
|
||||
void addToInitializer(Consumer<WasmFunction> initializerContributor);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ public class WasmGlobal extends WasmEntity {
|
|||
private WasmType type;
|
||||
private WasmExpression initialValue;
|
||||
private boolean immutable;
|
||||
private String exportName;
|
||||
|
||||
public WasmGlobal(String name, WasmType type, WasmExpression initialValue) {
|
||||
this.name = name;
|
||||
|
@ -57,4 +58,12 @@ public class WasmGlobal extends WasmEntity {
|
|||
public void setImmutable(boolean immutable) {
|
||||
this.immutable = immutable;
|
||||
}
|
||||
|
||||
public String getExportName() {
|
||||
return exportName;
|
||||
}
|
||||
|
||||
public void setExportName(String exportName) {
|
||||
this.exportName = exportName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public class WasmModule {
|
|||
public final WasmCollection<WasmGlobal> globals = new WasmCollection<>();
|
||||
public final WasmCollection<WasmCompositeType> types = new WasmCollection<>();
|
||||
public final WasmCollection<WasmTag> tags = new WasmCollection<>();
|
||||
public String memoryExportName = "memory";
|
||||
|
||||
public void add(WasmCustomSection customSection) {
|
||||
if (customSections.containsKey(customSection.getName())) {
|
||||
|
|
|
@ -49,6 +49,7 @@ public class WasmBinaryRenderer {
|
|||
|
||||
private static final int EXTERNAL_KIND_FUNCTION = 0;
|
||||
private static final int EXTERNAL_KIND_MEMORY = 2;
|
||||
private static final int EXTERNAL_KIND_GLOBAL = 3;
|
||||
private static final int EXTERNAL_KIND_TAG = 4;
|
||||
|
||||
private WasmBinaryWriter output;
|
||||
|
@ -241,7 +242,11 @@ public class WasmBinaryRenderer {
|
|||
.filter(tag -> tag.getExportName() != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
section.writeLEB(functions.size() + tags.size() + 1);
|
||||
var globals = module.globals.stream()
|
||||
.filter(global -> global.getExportName() != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
section.writeLEB(functions.size() + tags.size() + globals.size() + 1);
|
||||
for (var function : functions) {
|
||||
int functionIndex = module.functions.indexOf(function);
|
||||
|
||||
|
@ -257,9 +262,16 @@ public class WasmBinaryRenderer {
|
|||
section.writeByte(EXTERNAL_KIND_TAG);
|
||||
section.writeLEB(tagIndex);
|
||||
}
|
||||
for (var global : globals) {
|
||||
var index = module.globals.indexOf(global);
|
||||
section.writeAsciiString(global.getExportName());
|
||||
|
||||
section.writeByte(EXTERNAL_KIND_GLOBAL);
|
||||
section.writeLEB(index);
|
||||
}
|
||||
|
||||
// We also need to export the memory to make it accessible
|
||||
section.writeAsciiString("memory");
|
||||
section.writeAsciiString(module.memoryExportName);
|
||||
section.writeByte(EXTERNAL_KIND_MEMORY);
|
||||
section.writeLEB(0);
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2024 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.wasm.transformation.gc;
|
||||
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
|
||||
public class EntryPointTransformation implements ClassHolderTransformer {
|
||||
private static final MethodDescriptor MAIN_METHOD = new MethodDescriptor("main", String[].class, void.class);
|
||||
private String entryPoint;
|
||||
private String entryPointName;
|
||||
|
||||
public void setEntryPoint(String entryPoint) {
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
|
||||
public void setEntryPointName(String entryPointName) {
|
||||
this.entryPointName = entryPointName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
if (cls.getName().equals(entryPoint)) {
|
||||
var mainMethod = cls.getMethod(MAIN_METHOD);
|
||||
if (mainMethod != null) {
|
||||
mainMethod.getAnnotations().add(new AnnotationHolder("org.teavm.jso.JSExport"));
|
||||
|
||||
var methodAnnot = new AnnotationHolder("org.teavm.jso.JSMethod");
|
||||
methodAnnot.getValues().put("value", new AnnotationValue(entryPointName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -381,7 +381,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
return;
|
||||
}
|
||||
|
||||
processEntryPoint();
|
||||
target.setEntryPoint(entryPoint, entryPointName);
|
||||
dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported());
|
||||
dependencyAnalyzer.setInterruptor(() -> {
|
||||
int progress = dependencyAnalyzer.getReachableClasses().size();
|
||||
|
@ -390,6 +390,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
});
|
||||
target.contributeDependencies(dependencyAnalyzer);
|
||||
dependencyAnalyzer.initDependencies();
|
||||
processEntryPoint();
|
||||
if (target.needsSystemArrayCopyOptimization()) {
|
||||
dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener());
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ public interface TeaVMTarget {
|
|||
|
||||
List<DependencyListener> getDependencyListeners();
|
||||
|
||||
default void setEntryPoint(String entryPoint, String name) {
|
||||
}
|
||||
|
||||
void setController(TeaVMTargetController controller);
|
||||
|
||||
List<TeaVMHostExtension> getHostExtensions();
|
||||
|
|
|
@ -15,9 +15,14 @@
|
|||
*/
|
||||
|
||||
var TeaVM = TeaVM || {};
|
||||
TeaVM.wasm = function() {
|
||||
if (window && window.TeaVM === undefined) {
|
||||
window.TeaVM = TeaVM;
|
||||
}
|
||||
TeaVM.wasmGC = TeaVM.wasmGC || function() {
|
||||
let exports;
|
||||
let globalsCache = new Map();
|
||||
let stackDeobfuscator = null;
|
||||
let chromeExceptionRegex = / *at .+\.wasm:wasm-function\[[0-9]+]:0x([0-9a-f]+).*/;
|
||||
let getGlobalName = function(name) {
|
||||
let result = globalsCache.get(name);
|
||||
if (typeof result === "undefined") {
|
||||
|
@ -45,11 +50,11 @@ TeaVM.wasm = function() {
|
|||
this[javaExceptionSymbol] = javaException;
|
||||
}
|
||||
get message() {
|
||||
let exceptionMessage = exports.exceptionMessage;
|
||||
let exceptionMessage = exports["teavm.exceptionMessage"];
|
||||
if (typeof exceptionMessage === "function") {
|
||||
let message = exceptionMessage(this[javaExceptionSymbol]);
|
||||
if (message != null) {
|
||||
return stringToJava(message);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
return "(could not fetch message)";
|
||||
|
@ -59,8 +64,8 @@ TeaVM.wasm = function() {
|
|||
function dateImports(imports) {
|
||||
imports.teavmDate = {
|
||||
currentTimeMillis: () => new Date().getTime(),
|
||||
dateToString: timestamp => stringToJava(new Date(timestamp).toString()),
|
||||
getYear: timestamp =>new Date(timestamp).getFullYear(),
|
||||
dateToString: timestamp => new Date(timestamp).toString(),
|
||||
getYear: timestamp => new Date(timestamp).getFullYear(),
|
||||
setYear(timestamp, year) {
|
||||
let date = new Date(timestamp);
|
||||
date.setFullYear(year);
|
||||
|
@ -108,12 +113,16 @@ TeaVM.wasm = function() {
|
|||
|
||||
function coreImports(imports) {
|
||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
if (typeof exports.reportGarbageCollectedValue === "function") {
|
||||
exports.reportGarbageCollectedValue(heldValue)
|
||||
let report = exports["teavm.reportGarbageCollectedValue"];
|
||||
if (typeof report === "function") {
|
||||
report(heldValue)
|
||||
}
|
||||
});
|
||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
exports.reportGarbageCollectedString(heldValue);
|
||||
let report = exports["teavm.reportGarbageCollectedString"];
|
||||
if (typeof report === "function") {
|
||||
report(heldValue);
|
||||
}
|
||||
});
|
||||
imports.teavm = {
|
||||
createWeakRef(value, heldValue) {
|
||||
|
@ -129,7 +138,28 @@ TeaVM.wasm = function() {
|
|||
stringFinalizationRegistry.register(value, heldValue)
|
||||
return weakRef;
|
||||
},
|
||||
stringDeref: weakRef => weakRef.deref()
|
||||
stringDeref: weakRef => weakRef.deref(),
|
||||
currentStackTrace() {
|
||||
if (stackDeobfuscator) {
|
||||
return;
|
||||
}
|
||||
let reportCallFrame = exports["teavm.reportCallFrame"];
|
||||
if (typeof reportCallFrame !== "function") {
|
||||
return;
|
||||
}
|
||||
let stack = new Error().stack;
|
||||
for (let line in stack.split("\n")) {
|
||||
let match = chromeExceptionRegex.exec(line);
|
||||
if (match !== null) {
|
||||
let address = parseInt(match.groups[1], 16);
|
||||
let frames = stackDeobfuscator(address);
|
||||
for (let frame of frames) {
|
||||
let line = frame.line;
|
||||
reportCallFrame(file, method, cls, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -137,6 +167,7 @@ TeaVM.wasm = function() {
|
|||
let javaObjectSymbol = Symbol("javaObject");
|
||||
let functionsSymbol = Symbol("functions");
|
||||
let functionOriginSymbol = Symbol("functionOrigin");
|
||||
let wrapperCallMarkerSymbol = Symbol("wrapperCallMarker");
|
||||
|
||||
let jsWrappers = new WeakMap();
|
||||
let javaWrappers = new WeakMap();
|
||||
|
@ -182,7 +213,7 @@ TeaVM.wasm = function() {
|
|||
}
|
||||
function javaExceptionToJs(e) {
|
||||
if (e instanceof WebAssembly.Exception) {
|
||||
let tag = exports["javaException"];
|
||||
let tag = exports["teavm.javaException"];
|
||||
if (e.is(tag)) {
|
||||
let javaException = e.getArg(tag, 0);
|
||||
let extracted = extractException(javaException);
|
||||
|
@ -226,6 +257,22 @@ TeaVM.wasm = function() {
|
|||
rethrowJsAsJava(e);
|
||||
}
|
||||
}
|
||||
function defineFunction(fn) {
|
||||
let params = [];
|
||||
for (let i = 0; i < fn.length; ++i) {
|
||||
params.push("p" + i);
|
||||
}
|
||||
let paramsAsString = params.length === 0 ? "" : params.join(", ");
|
||||
return new Function("rethrowJavaAsJs", "fn", `
|
||||
return function(${paramsAsString}) {
|
||||
try {
|
||||
return fn(${paramsAsString});
|
||||
} catch (e) {
|
||||
rethrowJavaAsJs(e);
|
||||
}
|
||||
};
|
||||
`)(rethrowJavaAsJs, fn);
|
||||
}
|
||||
imports.teavmJso = {
|
||||
emptyString: () => "",
|
||||
stringFromCharCode: code => String.fromCharCode(code),
|
||||
|
@ -247,16 +294,68 @@ TeaVM.wasm = function() {
|
|||
rethrowJsAsJava(e);
|
||||
}
|
||||
},
|
||||
createClass(name) {
|
||||
let fn = new Function(
|
||||
"javaObjectSymbol",
|
||||
"functionsSymbol",
|
||||
`return function JavaClass_${sanitizeName(name)}(javaObject) {
|
||||
this[javaObjectSymbol] = javaObject;
|
||||
this[functionsSymbol] = null;
|
||||
};`
|
||||
);
|
||||
return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol);
|
||||
createClass(name, parent, constructor) {
|
||||
name = sanitizeName(name);
|
||||
if (parent === null) {
|
||||
let fn = new Function(
|
||||
"javaObjectSymbol",
|
||||
"functionsSymbol",
|
||||
"wrapperCallMarker",
|
||||
"constructor",
|
||||
"rethrowJavaAsJs",
|
||||
`let fn;
|
||||
fn = function ${name}(marker, javaObject) {
|
||||
if (marker === wrapperCallMarker) {
|
||||
this[javaObjectSymbol] = javaObject;
|
||||
this[functionsSymbol] = null;
|
||||
} else if (constructor === null) {
|
||||
throw new Error("This class can't be instantiated directly");
|
||||
} else {
|
||||
try {
|
||||
return fn(wrapperCallMarker, constructor(arguments));
|
||||
} catch (e) {
|
||||
rethrowJavaAsJs(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
let boundFn = function(javaObject) { return fn.call(this, wrapperCallMarker, javaObject); };
|
||||
boundFn[wrapperCallMarker] = fn;
|
||||
boundFn.prototype = fn.prototype;
|
||||
return boundFn;`
|
||||
);
|
||||
return fn(javaObjectSymbol, functionsSymbol, wrapperCallMarkerSymbol, constructor, rethrowJavaAsJs);
|
||||
} else {
|
||||
let fn = new Function(
|
||||
"parent",
|
||||
"wrapperCallMarker",
|
||||
"constructor",
|
||||
"rethrowJavaAsJs",
|
||||
`let fn
|
||||
fn = function ${name}(marker, javaObject) {
|
||||
if (marker === wrapperCallMarker) {
|
||||
parent.call(this, javaObject);
|
||||
} else if (constructor === null) {
|
||||
throw new Error("This class can't be instantiated directly");
|
||||
} else {
|
||||
try {
|
||||
return fn(wrapperCallMarker, constructor(arguments));
|
||||
} catch (e) {
|
||||
rethrowJavaAsJs(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
fn.prototype = Object.create(parent);
|
||||
fn.prototype.constructor = parent;
|
||||
let boundFn = function(javaObject) { return fn.call(this, wrapperCallMarker, javaObject); };
|
||||
boundFn[wrapperCallMarker] = fn;
|
||||
boundFn.prototype = fn.prototype;
|
||||
return fn;`
|
||||
);
|
||||
return fn(parent, wrapperCallMarkerSymbol, constructor, rethrowJavaAsJs);
|
||||
}
|
||||
},
|
||||
exportClass(cls) {
|
||||
return cls[wrapperCallMarkerSymbol];
|
||||
},
|
||||
defineMethod(cls, name, fn) {
|
||||
let params = [];
|
||||
|
@ -274,6 +373,10 @@ TeaVM.wasm = function() {
|
|||
};
|
||||
`)(rethrowJavaAsJs, fn);
|
||||
},
|
||||
defineStaticMethod(cls, name, fn) {
|
||||
cls[name] = defineFunction(fn);
|
||||
},
|
||||
defineFunction: defineFunction,
|
||||
defineProperty(cls, name, getFn, setFn) {
|
||||
let descriptor = {
|
||||
get() {
|
||||
|
@ -295,6 +398,27 @@ TeaVM.wasm = function() {
|
|||
}
|
||||
Object.defineProperty(cls.prototype, name, descriptor);
|
||||
},
|
||||
defineStaticProperty(cls, name, getFn, setFn) {
|
||||
let descriptor = {
|
||||
get() {
|
||||
try {
|
||||
return getFn();
|
||||
} catch (e) {
|
||||
rethrowJavaAsJs(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (setFn !== null) {
|
||||
descriptor.set = function(value) {
|
||||
try {
|
||||
setFn(value);
|
||||
} catch (e) {
|
||||
rethrowJavaAsJs(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.defineProperty(cls, name, descriptor);
|
||||
},
|
||||
javaObjectToJS(instance, cls) {
|
||||
let existing = jsWrappers.get(instance);
|
||||
if (typeof existing != "undefined") {
|
||||
|
@ -474,41 +598,26 @@ TeaVM.wasm = function() {
|
|||
options.installImports(importObj);
|
||||
}
|
||||
|
||||
return WebAssembly.instantiateStreaming(fetch(path), importObj).then((obj => {
|
||||
let teavm = {};
|
||||
teavm.main = createMain(obj.instance);
|
||||
teavm.instance = obj.instance;
|
||||
return teavm;
|
||||
}));
|
||||
}
|
||||
|
||||
function stringToJava(str) {
|
||||
let sb = exports.createStringBuilder();
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
exports.appendChar(sb, str.charCodeAt(i));
|
||||
}
|
||||
return exports.buildString(sb);
|
||||
}
|
||||
|
||||
function createMain(instance) {
|
||||
return args => {
|
||||
if (typeof args === "undefined") {
|
||||
args = [];
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
exports = instance.exports;
|
||||
let javaArgs = exports.createStringArray(args.length);
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
exports.setToStringArray(javaArgs, i, stringToJava(args[i]));
|
||||
}
|
||||
try {
|
||||
exports.main(javaArgs);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return WebAssembly.instantiateStreaming(fetch(path), importObj)
|
||||
.then(r => {
|
||||
exports = r.instance.exports;
|
||||
let userExports = {};
|
||||
let teavm = {
|
||||
exports: userExports,
|
||||
instance: r.instance,
|
||||
module: r.module
|
||||
};
|
||||
for (let key in r.instance.exports) {
|
||||
let exportObj = r.instance.exports[key];
|
||||
if (exportObj instanceof WebAssembly.Global) {
|
||||
Object.defineProperty(userExports, key, {
|
||||
get: () => exportObj.value
|
||||
});
|
||||
}
|
||||
}
|
||||
return teavm;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { load };
|
||||
return { load, defaults };
|
||||
}();
|
||||
|
|
|
@ -72,7 +72,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
var hasExportedMembers = false;
|
||||
hasExportedMembers |= exportClassInstanceMembers(classReader);
|
||||
if (!className.equals(context.getEntryPoint())) {
|
||||
var name = "$rt_export_class_ " + getClassAliasName(classReader) + "_" + lastExportIndex++;
|
||||
var name = "$rt_export_class_" + getClassAliasName(classReader) + "_" + lastExportIndex++;
|
||||
hasExportedMembers |= exportClassStaticMembers(classReader, name);
|
||||
if (hasExportedMembers) {
|
||||
exportedNamesByClass.put(className, name);
|
||||
|
@ -213,10 +213,12 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
}
|
||||
writer.append(")").ws().appendBlockStart();
|
||||
if (method != null) {
|
||||
writer.appendClass(cls.getName()).append(".call(this);").softNewLine();
|
||||
writer.appendMethod(method).append("(this");
|
||||
writer.append("return ").appendMethod(method).append("(");
|
||||
for (var i = 0; i < method.parameterCount(); ++i) {
|
||||
writer.append(",").ws().append("p" + i);
|
||||
if (i > 0) {
|
||||
writer.append(",").ws();
|
||||
}
|
||||
writer.append("p" + i);
|
||||
}
|
||||
writer.append(");").softNewLine();
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 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;
|
||||
|
||||
public @interface JSClassObjectToExpose {
|
||||
}
|
|
@ -161,7 +161,6 @@ public final class JSMethods {
|
|||
|
||||
public static final String JS_MARSHALLABLE = JSMarshallable.class.getName();
|
||||
public static final MethodDescriptor MARSHALL_TO_JS = new MethodDescriptor("marshallToJs", JS_OBJECT);
|
||||
public static final MethodReference MARSHALL_TO_JS_REF = new MethodReference(JS_MARSHALLABLE, MARSHALL_TO_JS);
|
||||
|
||||
static {
|
||||
for (int i = 0; i < INVOKE_METHODS.length; ++i) {
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.teavm.model.Program;
|
|||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.ConstructInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
@ -120,11 +121,14 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
}
|
||||
|
||||
exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod);
|
||||
exportStaticMethods(cls, context.getDiagnostics());
|
||||
var hasStaticMethods = exportStaticMethods(cls, context.getDiagnostics());
|
||||
|
||||
if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty()) {
|
||||
cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName()));
|
||||
}
|
||||
if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty() || hasStaticMethods) {
|
||||
cls.getAnnotations().add(new AnnotationHolder(JSClassObjectToExpose.class.getName()));
|
||||
}
|
||||
|
||||
if (wasmGC && (!exposedClass.methods.isEmpty() || isJavaScriptClass(cls))) {
|
||||
var createWrapperMethod = new MethodHolder(JSMethods.MARSHALL_TO_JS);
|
||||
|
@ -132,7 +136,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
createWrapperMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||
cls.addMethod(createWrapperMethod);
|
||||
|
||||
if (isJavaScriptImplementation(cls)) {
|
||||
if (!isJavaScriptClass(cls) || isJavaScriptImplementation(cls)) {
|
||||
cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE);
|
||||
}
|
||||
}
|
||||
|
@ -147,13 +151,17 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
MethodReference methodRef = new MethodReference(classHolder.getName(), method);
|
||||
CallLocation callLocation = new CallLocation(methodRef);
|
||||
|
||||
var isConstructor = entry.getKey().getName().equals("<init>");
|
||||
var paramCount = method.parameterCount();
|
||||
if (export.vararg) {
|
||||
--paramCount;
|
||||
}
|
||||
if (isConstructor) {
|
||||
--paramCount;
|
||||
}
|
||||
var exportedMethodSignature = new ValueType[paramCount + 2];
|
||||
Arrays.fill(exportedMethodSignature, JSMethods.JS_OBJECT);
|
||||
if (methodRef.getReturnType() == ValueType.VOID) {
|
||||
if (methodRef.getReturnType() == ValueType.VOID && !isConstructor) {
|
||||
exportedMethodSignature[exportedMethodSignature.length - 1] = ValueType.VOID;
|
||||
}
|
||||
MethodDescriptor exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
||||
|
@ -164,7 +172,9 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
Program program = new Program();
|
||||
exportedMethod.setProgram(program);
|
||||
program.createVariable();
|
||||
program.createVariable();
|
||||
if (!isConstructor) {
|
||||
program.createVariable();
|
||||
}
|
||||
|
||||
BasicBlock basicBlock = program.createBasicBlock();
|
||||
List<Instruction> marshallInstructions = new ArrayList<>();
|
||||
|
@ -189,33 +199,52 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
basicBlock.addAll(marshallInstructions);
|
||||
marshallInstructions.clear();
|
||||
|
||||
var unmarshalledInstance = new InvokeInstruction();
|
||||
unmarshalledInstance.setType(InvocationType.SPECIAL);
|
||||
unmarshalledInstance.setReceiver(program.createVariable());
|
||||
unmarshalledInstance.setArguments(program.variableAt(1));
|
||||
unmarshalledInstance.setMethod(new MethodReference(JSWrapper.class,
|
||||
"unmarshallJavaFromJs", JSObject.class, Object.class));
|
||||
basicBlock.add(unmarshalledInstance);
|
||||
Variable receiverToPass;
|
||||
if (isConstructor) {
|
||||
var create = new ConstructInstruction();
|
||||
create.setReceiver(program.createVariable());
|
||||
create.setType(classHolder.getName());
|
||||
basicBlock.add(create);
|
||||
receiverToPass = create.getReceiver();
|
||||
} else {
|
||||
var unmarshalledInstance = new InvokeInstruction();
|
||||
unmarshalledInstance.setType(InvocationType.SPECIAL);
|
||||
unmarshalledInstance.setReceiver(program.createVariable());
|
||||
unmarshalledInstance.setArguments(program.variableAt(1));
|
||||
unmarshalledInstance.setMethod(new MethodReference(JSWrapper.class,
|
||||
"unmarshallJavaFromJs", JSObject.class, Object.class));
|
||||
basicBlock.add(unmarshalledInstance);
|
||||
|
||||
var castInstance = new CastInstruction();
|
||||
castInstance.setValue(unmarshalledInstance.getReceiver());
|
||||
castInstance.setReceiver(program.createVariable());
|
||||
castInstance.setWeak(true);
|
||||
castInstance.setTargetType(ValueType.object(classHolder.getName()));
|
||||
basicBlock.add(castInstance);
|
||||
var castInstance = new CastInstruction();
|
||||
castInstance.setValue(unmarshalledInstance.getReceiver());
|
||||
castInstance.setReceiver(program.createVariable());
|
||||
castInstance.setWeak(true);
|
||||
castInstance.setTargetType(ValueType.object(classHolder.getName()));
|
||||
basicBlock.add(castInstance);
|
||||
|
||||
receiverToPass = castInstance.getReceiver();
|
||||
}
|
||||
|
||||
InvokeInstruction invocation = new InvokeInstruction();
|
||||
invocation.setType(method.getName().equals("<init>") ? InvocationType.SPECIAL : InvocationType.VIRTUAL);
|
||||
invocation.setInstance(castInstance.getReceiver());
|
||||
invocation.setType(isConstructor ? InvocationType.SPECIAL : InvocationType.VIRTUAL);
|
||||
invocation.setInstance(receiverToPass);
|
||||
invocation.setMethod(methodRef);
|
||||
invocation.setArguments(variablesToPass);
|
||||
basicBlock.add(invocation);
|
||||
|
||||
ExitInstruction exit = new ExitInstruction();
|
||||
if (method.getResultType() != ValueType.VOID) {
|
||||
invocation.setReceiver(program.createVariable());
|
||||
exit.setValueToReturn(marshaller.wrapArgument(callLocation, invocation.getReceiver(),
|
||||
method.getResultType(), JSType.MIXED, false));
|
||||
if (method.getResultType() != ValueType.VOID || isConstructor) {
|
||||
Variable result;
|
||||
ValueType resultType;
|
||||
if (isConstructor) {
|
||||
result = receiverToPass;
|
||||
resultType = ValueType.object(classHolder.getName());
|
||||
} else {
|
||||
result = program.createVariable();
|
||||
invocation.setReceiver(result);
|
||||
resultType = method.getResultType();
|
||||
}
|
||||
exit.setValueToReturn(marshaller.wrapArgument(callLocation, result, resultType, JSType.JAVA, false));
|
||||
basicBlock.addAll(marshallInstructions);
|
||||
marshallInstructions.clear();
|
||||
}
|
||||
|
@ -230,13 +259,15 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
}
|
||||
}
|
||||
|
||||
private void exportStaticMethods(ClassHolder classHolder, Diagnostics diagnostics) {
|
||||
private boolean exportStaticMethods(ClassHolder classHolder, Diagnostics diagnostics) {
|
||||
int index = 0;
|
||||
var hasMethods = false;
|
||||
for (var method : classHolder.getMethods().toArray(new MethodHolder[0])) {
|
||||
if (!method.hasModifier(ElementModifier.STATIC)
|
||||
|| method.getAnnotations().get(JSExport.class.getName()) == null) {
|
||||
continue;
|
||||
}
|
||||
hasMethods = true;
|
||||
|
||||
var paramCount = method.parameterCount();
|
||||
var vararg = method.hasModifier(ElementModifier.VARARGS);
|
||||
|
@ -246,6 +277,9 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
var callLocation = new CallLocation(method.getReference());
|
||||
var exportedMethodSignature = new ValueType[paramCount + 1];
|
||||
Arrays.fill(exportedMethodSignature, JSMethods.JS_OBJECT);
|
||||
if (method.getResultType() == ValueType.VOID) {
|
||||
exportedMethodSignature[exportedMethodSignature.length - 1] = ValueType.VOID;
|
||||
}
|
||||
var exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
||||
exportedMethodSignature);
|
||||
var exportedMethod = new MethodHolder(exportedMethodDesc);
|
||||
|
@ -287,7 +321,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
if (method.getResultType() != ValueType.VOID) {
|
||||
invocation.setReceiver(program.createVariable());
|
||||
exit.setValueToReturn(marshaller.wrapArgument(callLocation, invocation.getReceiver(),
|
||||
method.getResultType(), JSType.MIXED, false));
|
||||
method.getResultType(), JSType.JAVA, false));
|
||||
basicBlock.addAll(marshallInstructions);
|
||||
marshallInstructions.clear();
|
||||
}
|
||||
|
@ -298,6 +332,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
var export = createMethodExport(method);
|
||||
exportedMethod.getAnnotations().add(createExportAnnotation(export));
|
||||
}
|
||||
return hasMethods;
|
||||
}
|
||||
|
||||
private void transformVarargParam(Variable[] variablesToPass, Program program,
|
||||
|
|
|
@ -34,6 +34,19 @@ public class JSTypeHelper {
|
|||
knownJavaScriptClasses.put(JSObject.class.getName(), true);
|
||||
}
|
||||
|
||||
public JSType mapType(ValueType type) {
|
||||
if (type instanceof ValueType.Object) {
|
||||
var className = ((ValueType.Object) type).getClassName();
|
||||
if (isJavaScriptClass(className)) {
|
||||
return JSType.JS;
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
var elementType = mapType(((ValueType.Array) type).getItemType());
|
||||
return JSType.arrayOf(elementType);
|
||||
}
|
||||
return JSType.JAVA;
|
||||
}
|
||||
|
||||
public boolean isJavaScriptClass(String className) {
|
||||
Boolean isJsClass = knownJavaScriptClasses.get(className);
|
||||
if (isJsClass == null) {
|
||||
|
|
|
@ -38,16 +38,7 @@ class JSTypeInference extends BaseTypeInference<JSType> {
|
|||
|
||||
@Override
|
||||
protected JSType mapType(ValueType type) {
|
||||
if (type instanceof ValueType.Object) {
|
||||
var className = ((ValueType.Object) type).getClassName();
|
||||
if (typeHelper.isJavaScriptClass(className)) {
|
||||
return JSType.JS;
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
var elementType = mapType(((ValueType.Array) type).getItemType());
|
||||
return JSType.arrayOf(elementType);
|
||||
}
|
||||
return JSType.JAVA;
|
||||
return typeHelper.mapType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
|
|||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.JS;
|
||||
import org.teavm.jso.impl.JSBodyRepository;
|
||||
import org.teavm.jso.impl.JSClassObjectToExpose;
|
||||
import org.teavm.jso.impl.JSWrapper;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
|
@ -35,6 +36,12 @@ public final class WasmGCJso {
|
|||
wasmGCHost.addCustomTypeMapperFactory(new WasmGCJSTypeMapper());
|
||||
wasmGCHost.addIntrinsicFactory(new WasmGCJSBodyRenderer(jsBodyRepository, jsFunctions, commonGen));
|
||||
wasmGCHost.addGeneratorFactory(new WasmGCMarshallMethodGeneratorFactory(commonGen));
|
||||
wasmGCHost.addClassConsumer((context, className) -> {
|
||||
var cls = context.classes().get(className);
|
||||
if (cls != null && cls.getAnnotations().get(JSClassObjectToExpose.class.getName()) != null) {
|
||||
commonGen.getDefinedClass(WasmGCJsoContext.wrap(context), className);
|
||||
}
|
||||
});
|
||||
|
||||
var jsIntrinsic = new WasmGCJSIntrinsic(commonGen);
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "wrap", String.class, JSObject.class), jsIntrinsic);
|
||||
|
|
|
@ -32,15 +32,20 @@ import org.teavm.backend.wasm.model.expression.WasmCast;
|
|||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExternConversionType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
||||
import org.teavm.jso.JSClass;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.impl.AliasCollector;
|
||||
import org.teavm.jso.impl.JSBodyAstEmitter;
|
||||
import org.teavm.jso.impl.JSBodyBloatedEmitter;
|
||||
import org.teavm.jso.impl.JSBodyEmitter;
|
||||
import org.teavm.jso.impl.JSMarshallable;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
|
@ -51,6 +56,15 @@ class WasmGCJsoCommonGenerator {
|
|||
private boolean rethrowExported;
|
||||
private Map<String, WasmGlobal> stringsConstants = new HashMap<>();
|
||||
|
||||
private WasmFunction createClassFunction;
|
||||
private WasmFunction defineFunctionFunction;
|
||||
private WasmFunction defineMethodFunction;
|
||||
private WasmFunction defineStaticMethodFunction;
|
||||
private WasmFunction definePropertyFunction;
|
||||
private WasmFunction defineStaticPropertyFunction;
|
||||
private WasmFunction exportClassFunction;
|
||||
private Map<String, WasmGlobal> definedClasses = new HashMap<>();
|
||||
|
||||
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
|
||||
this.jsFunctions = jsFunctions;
|
||||
}
|
||||
|
@ -70,7 +84,7 @@ class WasmGCJsoCommonGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
void addInitializerPart(WasmGCJsoContext context, Consumer<WasmFunction> part) {
|
||||
private void addInitializerPart(WasmGCJsoContext context, Consumer<WasmFunction> part) {
|
||||
initialize(context);
|
||||
initializerParts.add(part);
|
||||
}
|
||||
|
@ -183,4 +197,277 @@ class WasmGCJsoCommonGenerator {
|
|||
});
|
||||
return new WasmGetGlobal(global);
|
||||
}
|
||||
|
||||
WasmGlobal getDefinedClass(WasmGCJsoContext context, String className) {
|
||||
return definedClasses.computeIfAbsent(className, n -> defineClass(context, n));
|
||||
}
|
||||
|
||||
private WasmGlobal defineClass(WasmGCJsoContext context, String className) {
|
||||
var name = context.names().topLevel(context.names().suggestForClass(className + "@js"));
|
||||
var global = new WasmGlobal(name, WasmType.Reference.EXTERN, new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||
context.module().globals.add(global);
|
||||
|
||||
var cls = context.classes().get(className);
|
||||
var expressions = new ArrayList<WasmExpression>();
|
||||
var isModule = context.entryPoint().equals(className);
|
||||
|
||||
var members = AliasCollector.collectMembers(cls, AliasCollector::isInstanceMember);
|
||||
defineMethods(context, members, cls, global, expressions);
|
||||
defineProperties(context, members, cls, global, expressions);
|
||||
|
||||
var staticMembers = AliasCollector.collectMembers(cls, AliasCollector::isStaticMember);
|
||||
defineStaticMethods(context, staticMembers, cls, global, expressions, isModule);
|
||||
defineStaticProperties(context, staticMembers, cls, global, expressions);
|
||||
|
||||
var simpleName = className.substring(className.lastIndexOf('.') + 1);
|
||||
var javaClassName = context.strings().getStringConstant(simpleName);
|
||||
var jsClassName = stringToJs(context, new WasmGetGlobal(javaClassName.global));
|
||||
|
||||
var exportedParent = parentExportedClass(context, cls.getParent());
|
||||
var jsExportedParent = exportedParent != null
|
||||
? new WasmGetGlobal(getDefinedClass(context, exportedParent))
|
||||
: new WasmNullConstant(WasmType.Reference.EXTERN);
|
||||
|
||||
var needsExport = !className.equals(context.entryPoint())
|
||||
&& (!staticMembers.methods.isEmpty() || !staticMembers.properties.isEmpty());
|
||||
WasmExpression constructor;
|
||||
if (members.constructor != null) {
|
||||
var function = context.functions().forStaticMethod(members.constructor);
|
||||
constructor = new WasmFunctionReference(function);
|
||||
needsExport = true;
|
||||
} else {
|
||||
constructor = new WasmNullConstant(WasmType.Reference.FUNC);
|
||||
}
|
||||
var createClass = new WasmCall(createClassFunction(context), jsClassName, jsExportedParent, constructor);
|
||||
expressions.add(0, new WasmSetGlobal(global, createClass));
|
||||
if (needsExport) {
|
||||
exportClass(context, cls, global, expressions);
|
||||
}
|
||||
|
||||
context.addToInitializer(f -> f.getBody().addAll(expressions));
|
||||
return global;
|
||||
}
|
||||
|
||||
private void defineMethods(WasmGCJsoContext context, AliasCollector.Members members, ClassReader cls,
|
||||
WasmGlobal global, List<WasmExpression> expressions) {
|
||||
for (var aliasEntry : members.methods.entrySet()) {
|
||||
if (!aliasEntry.getValue().getClassName().equals(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
var fn = context.functions().forStaticMethod(aliasEntry.getValue());
|
||||
fn.setReferenced(true);
|
||||
var methodName = context.strings().getStringConstant(aliasEntry.getKey());
|
||||
var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global));
|
||||
var defineMethod = new WasmCall(defineMethodFunction(context), new WasmGetGlobal(global),
|
||||
jsMethodName, new WasmFunctionReference(fn));
|
||||
expressions.add(defineMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private void defineProperties(WasmGCJsoContext context, AliasCollector.Members members, ClassReader cls,
|
||||
WasmGlobal global, List<WasmExpression> expressions) {
|
||||
for (var aliasEntry : members.properties.entrySet()) {
|
||||
var property = aliasEntry.getValue();
|
||||
if (!property.getter.getClassName().equals(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
var getter = context.functions().forStaticMethod(property.getter);
|
||||
getter.setReferenced(true);
|
||||
WasmFunction setter = null;
|
||||
if (property.setter != null) {
|
||||
setter = context.functions().forStaticMethod(property.setter);
|
||||
setter.setReferenced(true);
|
||||
}
|
||||
var setterRef = setter != null
|
||||
? new WasmFunctionReference(setter)
|
||||
: new WasmNullConstant(WasmType.Reference.FUNC);
|
||||
var methodName = context.strings().getStringConstant(aliasEntry.getKey());
|
||||
var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global));
|
||||
var defineProperty = new WasmCall(definePropertyFunction(context), new WasmGetGlobal(global),
|
||||
jsMethodName, new WasmFunctionReference(getter), setterRef);
|
||||
expressions.add(defineProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private void defineStaticMethods(WasmGCJsoContext context, AliasCollector.Members members, ClassReader cls,
|
||||
WasmGlobal global, List<WasmExpression> expressions, boolean isModule) {
|
||||
for (var aliasEntry : members.methods.entrySet()) {
|
||||
if (!aliasEntry.getValue().getClassName().equals(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
var fn = context.functions().forStaticMethod(aliasEntry.getValue());
|
||||
fn.setReferenced(true);
|
||||
if (isModule) {
|
||||
var globalName = context.names().topLevel("teavm.js.export.function@" + aliasEntry.getKey());
|
||||
var functionGlobal = new WasmGlobal(globalName, WasmType.Reference.EXTERN,
|
||||
new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||
functionGlobal.setExportName(aliasEntry.getKey());
|
||||
context.module().globals.add(functionGlobal);
|
||||
fn.setReferenced(true);
|
||||
var exportedFn = new WasmCall(defineFunctionFunction(context), new WasmFunctionReference(fn));
|
||||
expressions.add(new WasmSetGlobal(functionGlobal, exportedFn));
|
||||
}
|
||||
var methodName = context.strings().getStringConstant(aliasEntry.getKey());
|
||||
var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global));
|
||||
var defineMethod = new WasmCall(defineStaticMethodFunction(context), new WasmGetGlobal(global),
|
||||
jsMethodName, new WasmFunctionReference(fn));
|
||||
expressions.add(defineMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private void defineStaticProperties(WasmGCJsoContext context, AliasCollector.Members members, ClassReader cls,
|
||||
WasmGlobal global, List<WasmExpression> expressions) {
|
||||
for (var aliasEntry : members.properties.entrySet()) {
|
||||
var property = aliasEntry.getValue();
|
||||
if (!property.getter.getClassName().equals(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
var getter = context.functions().forStaticMethod(property.getter);
|
||||
getter.setReferenced(true);
|
||||
WasmFunction setter = null;
|
||||
if (property.setter != null) {
|
||||
setter = context.functions().forStaticMethod(property.setter);
|
||||
setter.setReferenced(true);
|
||||
}
|
||||
var setterRef = setter != null
|
||||
? new WasmFunctionReference(setter)
|
||||
: new WasmNullConstant(WasmType.Reference.FUNC);
|
||||
var methodName = context.strings().getStringConstant(aliasEntry.getKey());
|
||||
var jsMethodName = stringToJs(context, new WasmGetGlobal(methodName.global));
|
||||
var defineProperty = new WasmCall(defineStaticPropertyFunction(context), new WasmGetGlobal(global),
|
||||
jsMethodName, new WasmFunctionReference(getter), setterRef);
|
||||
expressions.add(defineProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private void exportClass(WasmGCJsoContext context, ClassReader cls, WasmGlobal global,
|
||||
List<WasmExpression> expressions) {
|
||||
var exportName = getClassAliasName(cls);
|
||||
var globalName = context.names().topLevel("teavm.js.export.class@" + exportName);
|
||||
var exportGlobal = new WasmGlobal(globalName, WasmType.Reference.EXTERN,
|
||||
new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||
exportGlobal.setExportName(exportName);
|
||||
context.module().globals.add(exportGlobal);
|
||||
|
||||
var exported = new WasmCall(exportClassFunction(context), new WasmGetGlobal(global));
|
||||
expressions.add(new WasmSetGlobal(exportGlobal, exported));
|
||||
}
|
||||
|
||||
private String parentExportedClass(WasmGCJsoContext context, String className) {
|
||||
while (className != null) {
|
||||
var cls = context.classes().get(className);
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
if (cls.getInterfaces().contains(JSMarshallable.class.getName())) {
|
||||
return className;
|
||||
}
|
||||
className = cls.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private WasmFunction createClassFunction(WasmGCJsoContext context) {
|
||||
if (createClassFunction == null) {
|
||||
createClassFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC));
|
||||
createClassFunction.setName(context.names().suggestForClass("teavm.jso@createClass"));
|
||||
createClassFunction.setImportName("createClass");
|
||||
createClassFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(createClassFunction);
|
||||
}
|
||||
return createClassFunction;
|
||||
}
|
||||
|
||||
private WasmFunction defineFunctionFunction(WasmGCJsoContext context) {
|
||||
if (defineFunctionFunction == null) {
|
||||
defineFunctionFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
||||
WasmType.Reference.FUNC));
|
||||
defineFunctionFunction.setName(context.names().suggestForClass("teavm.jso@defineFunction"));
|
||||
defineFunctionFunction.setImportName("defineFunction");
|
||||
defineFunctionFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(defineFunctionFunction);
|
||||
}
|
||||
return defineFunctionFunction;
|
||||
}
|
||||
|
||||
private WasmFunction defineMethodFunction(WasmGCJsoContext context) {
|
||||
if (defineMethodFunction == null) {
|
||||
defineMethodFunction = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC));
|
||||
defineMethodFunction.setName(context.names().suggestForClass("teavm.jso@defineMethod"));
|
||||
defineMethodFunction.setImportName("defineMethod");
|
||||
defineMethodFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(defineMethodFunction);
|
||||
}
|
||||
return defineMethodFunction;
|
||||
}
|
||||
|
||||
private WasmFunction defineStaticMethodFunction(WasmGCJsoContext context) {
|
||||
if (defineStaticMethodFunction == null) {
|
||||
defineStaticMethodFunction = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC));
|
||||
defineStaticMethodFunction.setName(context.names().suggestForClass("teavm.jso@defineStaticMethod"));
|
||||
defineStaticMethodFunction.setImportName("defineStaticMethod");
|
||||
defineStaticMethodFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(defineStaticMethodFunction);
|
||||
}
|
||||
return defineStaticMethodFunction;
|
||||
}
|
||||
|
||||
private WasmFunction definePropertyFunction(WasmGCJsoContext context) {
|
||||
if (definePropertyFunction == null) {
|
||||
definePropertyFunction = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC,
|
||||
WasmType.Reference.FUNC));
|
||||
definePropertyFunction.setName(context.names().suggestForClass("teavm.jso@defineProperty"));
|
||||
definePropertyFunction.setImportName("defineProperty");
|
||||
definePropertyFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(definePropertyFunction);
|
||||
}
|
||||
return definePropertyFunction;
|
||||
}
|
||||
|
||||
private WasmFunction defineStaticPropertyFunction(WasmGCJsoContext context) {
|
||||
if (defineStaticPropertyFunction == null) {
|
||||
defineStaticPropertyFunction = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC,
|
||||
WasmType.Reference.FUNC));
|
||||
defineStaticPropertyFunction.setName(context.names().suggestForClass("teavm.jso@defineStaticProperty"));
|
||||
defineStaticPropertyFunction.setImportName("defineStaticProperty");
|
||||
defineStaticPropertyFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(defineStaticPropertyFunction);
|
||||
}
|
||||
return defineStaticPropertyFunction;
|
||||
}
|
||||
|
||||
private WasmFunction exportClassFunction(WasmGCJsoContext context) {
|
||||
if (exportClassFunction == null) {
|
||||
exportClassFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
||||
WasmType.Reference.EXTERN));
|
||||
exportClassFunction.setName(context.names().suggestForClass("teavm.jso@exportClass"));
|
||||
exportClassFunction.setImportName("exportClass");
|
||||
exportClassFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(exportClassFunction);
|
||||
}
|
||||
return exportClassFunction;
|
||||
}
|
||||
|
||||
private String getClassAliasName(ClassReader cls) {
|
||||
var name = cls.getSimpleName();
|
||||
if (name == null) {
|
||||
name = cls.getName().substring(cls.getName().lastIndexOf('.') + 1);
|
||||
}
|
||||
var jsExport = cls.getAnnotations().get(JSClass.class.getName());
|
||||
if (jsExport != null) {
|
||||
var nameValue = jsExport.getValue("name");
|
||||
if (nameValue != null) {
|
||||
var nameValueString = nameValue.getString();
|
||||
if (!nameValueString.isEmpty()) {
|
||||
name = nameValueString;
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.jso.impl.wasmgc;
|
|||
import java.util.function.Consumer;
|
||||
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||
import org.teavm.backend.wasm.gc.WasmGCClassConsumerContext;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||
|
@ -26,8 +27,11 @@ import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
|||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
|
||||
interface WasmGCJsoContext {
|
||||
ClassReaderSource classes();
|
||||
|
||||
WasmModule module();
|
||||
|
||||
WasmFunctionTypes functionTypes();
|
||||
|
@ -42,10 +46,17 @@ interface WasmGCJsoContext {
|
|||
|
||||
WasmTag exceptionTag();
|
||||
|
||||
String entryPoint();
|
||||
|
||||
void addToInitializer(Consumer<WasmFunction> initializerContributor);
|
||||
|
||||
static WasmGCJsoContext wrap(WasmGCIntrinsicContext context) {
|
||||
return new WasmGCJsoContext() {
|
||||
@Override
|
||||
public ClassReaderSource classes() {
|
||||
return context.hierarchy().getClassSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmModule module() {
|
||||
return context.module();
|
||||
|
@ -81,6 +92,11 @@ interface WasmGCJsoContext {
|
|||
return context.exceptionTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return context.entryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
|
@ -90,6 +106,11 @@ interface WasmGCJsoContext {
|
|||
|
||||
static WasmGCJsoContext wrap(WasmGCCustomGeneratorContext context) {
|
||||
return new WasmGCJsoContext() {
|
||||
@Override
|
||||
public ClassReaderSource classes() {
|
||||
return context.classes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmModule module() {
|
||||
return context.module();
|
||||
|
@ -125,6 +146,65 @@ interface WasmGCJsoContext {
|
|||
return context.exceptionTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return context.entryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static WasmGCJsoContext wrap(WasmGCClassConsumerContext context) {
|
||||
return new WasmGCJsoContext() {
|
||||
@Override
|
||||
public ClassReaderSource classes() {
|
||||
return context.classes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmModule module() {
|
||||
return context.module();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmFunctionTypes functionTypes() {
|
||||
return context.functionTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseWasmFunctionRepository functions() {
|
||||
return context.functions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCNameProvider names() {
|
||||
return context.names();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCStringProvider strings() {
|
||||
return context.strings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCTypeMapper typeMapper() {
|
||||
return context.typeMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmTag exceptionTag() {
|
||||
return context.exceptionTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return context.entryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
|
|
|
@ -15,31 +15,20 @@
|
|||
*/
|
||||
package org.teavm.jso.impl.wasmgc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
import org.teavm.jso.impl.AliasCollector;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator {
|
||||
private WasmGCJsoCommonGenerator commonGen;
|
||||
private WasmFunction javaObjectToJSFunction;
|
||||
private WasmFunction createClassFunction;
|
||||
private WasmFunction defineMethodFunction;
|
||||
private WasmFunction definePropertyFunction;
|
||||
|
||||
WasmGCMarshallMethodGenerator(WasmGCJsoCommonGenerator commonGen) {
|
||||
this.commonGen = commonGen;
|
||||
|
@ -52,8 +41,7 @@ class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator {
|
|||
var thisLocal = new WasmLocal(context.typeMapper().mapType(ValueType.object(method.getClassName())), "this");
|
||||
function.add(thisLocal);
|
||||
|
||||
var cls = context.classes().get(method.getClassName());
|
||||
var jsClassGlobal = defineClass(jsoContext, cls);
|
||||
var jsClassGlobal = commonGen.getDefinedClass(jsoContext, method.getClassName());
|
||||
var wrapperFunction = javaObjectToJSFunction(context);
|
||||
function.getBody().add(new WasmCall(wrapperFunction, new WasmGetLocal(thisLocal),
|
||||
new WasmGetGlobal(jsClassGlobal)));
|
||||
|
@ -71,91 +59,4 @@ class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator {
|
|||
return javaObjectToJSFunction;
|
||||
}
|
||||
|
||||
WasmGlobal defineClass(WasmGCJsoContext context, ClassReader cls) {
|
||||
var name = context.names().topLevel(context.names().suggestForClass(cls.getName()));
|
||||
var global = new WasmGlobal(name, WasmType.Reference.EXTERN, new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||
context.module().globals.add(global);
|
||||
|
||||
var expressions = new ArrayList<WasmExpression>();
|
||||
var className = context.strings().getStringConstant(cls.getName());
|
||||
var jsClassName = commonGen.stringToJs(context, new WasmGetGlobal(className.global));
|
||||
var createClass = new WasmCall(createClassFunction(context), jsClassName);
|
||||
expressions.add(new WasmSetGlobal(global, createClass));
|
||||
|
||||
var members = AliasCollector.collectMembers(cls, AliasCollector::isInstanceMember);
|
||||
for (var aliasEntry : members.methods.entrySet()) {
|
||||
if (!aliasEntry.getValue().getClassName().equals(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
var fn = context.functions().forStaticMethod(aliasEntry.getValue());
|
||||
fn.setReferenced(true);
|
||||
var methodName = context.strings().getStringConstant(aliasEntry.getKey());
|
||||
var jsMethodName = commonGen.stringToJs(context, new WasmGetGlobal(methodName.global));
|
||||
var defineMethod = new WasmCall(defineMethodFunction(context), new WasmGetGlobal(global),
|
||||
jsMethodName, new WasmFunctionReference(fn));
|
||||
expressions.add(defineMethod);
|
||||
}
|
||||
|
||||
for (var aliasEntry : members.properties.entrySet()) {
|
||||
var property = aliasEntry.getValue();
|
||||
if (!property.getter.getClassName().equals(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
var getter = context.functions().forStaticMethod(property.getter);
|
||||
getter.setReferenced(true);
|
||||
WasmFunction setter = null;
|
||||
if (property.setter != null) {
|
||||
setter = context.functions().forStaticMethod(property.setter);
|
||||
setter.setReferenced(true);
|
||||
}
|
||||
var setterRef = setter != null
|
||||
? new WasmFunctionReference(setter)
|
||||
: new WasmNullConstant(WasmType.Reference.FUNC);
|
||||
var methodName = context.strings().getStringConstant(aliasEntry.getKey());
|
||||
var jsMethodName = commonGen.stringToJs(context, new WasmGetGlobal(methodName.global));
|
||||
var defineProperty = new WasmCall(definePropertyFunction(context), new WasmGetGlobal(global),
|
||||
jsMethodName, new WasmFunctionReference(getter), setterRef);
|
||||
expressions.add(defineProperty);
|
||||
}
|
||||
|
||||
context.addToInitializer(f -> f.getBody().addAll(expressions));
|
||||
return global;
|
||||
}
|
||||
|
||||
private WasmFunction createClassFunction(WasmGCJsoContext context) {
|
||||
if (createClassFunction == null) {
|
||||
createClassFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
||||
WasmType.Reference.EXTERN));
|
||||
createClassFunction.setName(context.names().suggestForClass("teavm.jso@createClass"));
|
||||
createClassFunction.setImportName("createClass");
|
||||
createClassFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(createClassFunction);
|
||||
}
|
||||
return createClassFunction;
|
||||
}
|
||||
|
||||
private WasmFunction defineMethodFunction(WasmGCJsoContext context) {
|
||||
if (defineMethodFunction == null) {
|
||||
defineMethodFunction = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC));
|
||||
defineMethodFunction.setName(context.names().suggestForClass("teavm.jso@defineMethod"));
|
||||
defineMethodFunction.setImportName("defineMethod");
|
||||
defineMethodFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(defineMethodFunction);
|
||||
}
|
||||
return defineMethodFunction;
|
||||
}
|
||||
|
||||
private WasmFunction definePropertyFunction(WasmGCJsoContext context) {
|
||||
if (definePropertyFunction == null) {
|
||||
definePropertyFunction = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.EXTERN, WasmType.Reference.EXTERN, WasmType.Reference.FUNC,
|
||||
WasmType.Reference.FUNC));
|
||||
definePropertyFunction.setName(context.names().suggestForClass("teavm.jso@defineProperty"));
|
||||
definePropertyFunction.setImportName("defineProperty");
|
||||
definePropertyFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(definePropertyFunction);
|
||||
}
|
||||
return definePropertyFunction;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</head>
|
||||
<script type="text/javascript">
|
||||
function launch() {
|
||||
TeaVM.wasm.load("wasm-gc/benchmark.wasm").then(teavm => teavm.main());
|
||||
TeaVM.wasmGC.load("wasm-gc/benchmark.wasm").then(teavm => teavm.exports.main([]));
|
||||
}
|
||||
</script>
|
||||
<body onload="launch()">
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<script type="application/javascript">
|
||||
let runner = null;
|
||||
function init() {
|
||||
TeaVM.wasm.load("wasm-gc/pi.wasm", {
|
||||
TeaVM.wasmGC.load("wasm-gc/pi.wasm", {
|
||||
installImports(o) {
|
||||
function putwchar(ch) {
|
||||
$rt_putStdoutCustom(String.fromCharCode(ch));
|
||||
|
@ -39,13 +39,12 @@
|
|||
o.teavmConsole.putcharStdout = putwchar;
|
||||
},
|
||||
}).then(teavm => {
|
||||
this.instance = teavm.instance;
|
||||
runner = n => teavm.main([n.toString()]);
|
||||
runner = n => teavm.exports.main([n.toString()]);
|
||||
document.getElementById("run").disabled = false;
|
||||
})
|
||||
}
|
||||
function calculate() {
|
||||
var count = parseInt(document.getElementById("digit-count").value);
|
||||
let count = parseInt(document.getElementById("digit-count").value);
|
||||
runner(count);
|
||||
}
|
||||
init();
|
||||
|
|
|
@ -19,13 +19,20 @@ import java.io.File;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.teavm.backend.javascript.JSModuleType;
|
||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||
import org.teavm.backend.wasm.WasmDebugInfoLevel;
|
||||
import org.teavm.backend.wasm.WasmDebugInfoLocation;
|
||||
import org.teavm.backend.wasm.WasmGCTarget;
|
||||
import org.teavm.backend.wasm.disasm.Disassembler;
|
||||
import org.teavm.backend.wasm.disasm.DisassemblyHTMLWriter;
|
||||
import org.teavm.browserrunner.BrowserRunDescriptor;
|
||||
import org.teavm.browserrunner.BrowserRunner;
|
||||
import org.teavm.tooling.ConsoleTeaVMToolLog;
|
||||
|
@ -35,8 +42,19 @@ import org.teavm.vm.TeaVMOptimizationLevel;
|
|||
|
||||
public class ExportTest {
|
||||
private static File targetFile = new File(new File(System.getProperty("teavm.junit.target")), "jso-export");
|
||||
private static BrowserRunner runner = new BrowserRunner(
|
||||
targetFile,
|
||||
private static File jsTargetFile = new File(targetFile, "js");
|
||||
private static File wasmGCTargetFile = new File(targetFile, "wasm-gc");
|
||||
|
||||
private static boolean jsNeeded = Boolean.parseBoolean(System.getProperty("teavm.junit.js", "true"));
|
||||
private static boolean wasmGCNeeded = Boolean.parseBoolean(System.getProperty("teavm.junit.wasm-gc", "true"));
|
||||
private static BrowserRunner jsRunner = new BrowserRunner(
|
||||
jsTargetFile,
|
||||
"JAVASCRIPT",
|
||||
BrowserRunner.pickBrowser(System.getProperty("teavm.junit.js.runner")),
|
||||
false
|
||||
);
|
||||
private static BrowserRunner wasmGCRunner = new BrowserRunner(
|
||||
wasmGCTargetFile,
|
||||
"JAVASCRIPT",
|
||||
BrowserRunner.pickBrowser(System.getProperty("teavm.junit.js.runner")),
|
||||
false
|
||||
|
@ -44,12 +62,22 @@ public class ExportTest {
|
|||
|
||||
@BeforeClass
|
||||
public static void start() {
|
||||
runner.start();
|
||||
if (jsNeeded) {
|
||||
jsRunner.start();
|
||||
}
|
||||
if (wasmGCNeeded) {
|
||||
wasmGCRunner.start();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stop() {
|
||||
runner.stop();
|
||||
if (jsNeeded) {
|
||||
jsRunner.stop();
|
||||
}
|
||||
if (wasmGCNeeded) {
|
||||
wasmGCRunner.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -69,7 +97,7 @@ public class ExportTest {
|
|||
|
||||
@Test
|
||||
public void exportClassMembers() {
|
||||
testExport("exportClassMembers", ModuleWithExportedClassMembers.class);
|
||||
testExport("exportClassMembers", ModuleWithExportedClassMembers.class, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -79,24 +107,34 @@ public class ExportTest {
|
|||
|
||||
@Test
|
||||
public void exportClasses() {
|
||||
testExport("exportClasses", ModuleWithExportedClasses.class);
|
||||
testExport("exportClasses", ModuleWithExportedClasses.class, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void varargs() {
|
||||
testExport("varargs", ModuleWithVararg.class);
|
||||
testExport("varargs", ModuleWithVararg.class, true);
|
||||
}
|
||||
|
||||
private void testExport(String name, Class<?> moduleClass) {
|
||||
if (!Boolean.parseBoolean(System.getProperty("teavm.junit.js", "true"))) {
|
||||
return;
|
||||
testExport(name, moduleClass, false);
|
||||
}
|
||||
|
||||
private void testExport(String name, Class<?> moduleClass, boolean skipWasm) {
|
||||
if (jsNeeded) {
|
||||
testExportJs(name, moduleClass);
|
||||
}
|
||||
if (wasmGCNeeded && !skipWasm) {
|
||||
testExportWasmGC(name, moduleClass);
|
||||
}
|
||||
}
|
||||
|
||||
private void testExportJs(String name, Class<?> moduleClass) {
|
||||
try {
|
||||
var jsTarget = new JavaScriptTarget();
|
||||
jsTarget.setModuleType(JSModuleType.ES2015);
|
||||
jsTarget.setObfuscated(false);
|
||||
var teavm = new TeaVMBuilder(jsTarget).build();
|
||||
var outputDir = new File(targetFile, name);
|
||||
var outputDir = new File(jsTargetFile, name);
|
||||
teavm.installPlugins();
|
||||
teavm.setEntryPoint(moduleClass.getName());
|
||||
teavm.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED);
|
||||
|
@ -115,10 +153,64 @@ public class ExportTest {
|
|||
writer.write(" test().then(() => callback()).catch(e => callback(e));\n");
|
||||
writer.write("}\n");
|
||||
}
|
||||
var testProviderFile = new File(outputDir, "provider.js");
|
||||
try (var writer = new OutputStreamWriter(new FileOutputStream(testProviderFile), StandardCharsets.UTF_8)) {
|
||||
writer.write("import * as obj from '/tests/" + name + "/test.js';\n");
|
||||
writer.write("export default Promise.resolve(obj);");
|
||||
}
|
||||
|
||||
var descriptor = new BrowserRunDescriptor(name, "tests/" + name + "/runner.js", true,
|
||||
List.of("resources/org/teavm/jso/export/assert.js"), null);
|
||||
runner.runTest(descriptor);
|
||||
jsRunner.runTest(descriptor);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void testExportWasmGC(String name, Class<?> moduleClass) {
|
||||
try {
|
||||
var wasmGCTarget = new WasmGCTarget();
|
||||
wasmGCTarget.setObfuscated(false);
|
||||
wasmGCTarget.setDebugLocation(WasmDebugInfoLocation.EMBEDDED);
|
||||
wasmGCTarget.setDebugLevel(WasmDebugInfoLevel.DEOBFUSCATION);
|
||||
var teavm = new TeaVMBuilder(wasmGCTarget).build();
|
||||
var outputDir = new File(wasmGCTargetFile, name);
|
||||
teavm.installPlugins();
|
||||
teavm.setEntryPoint(moduleClass.getName());
|
||||
teavm.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED);
|
||||
outputDir.mkdirs();
|
||||
teavm.build(outputDir, "test.wasm");
|
||||
if (!teavm.getProblemProvider().getSevereProblems().isEmpty()) {
|
||||
var log = new ConsoleTeaVMToolLog(false);
|
||||
TeaVMProblemRenderer.describeProblems(teavm, log);
|
||||
throw new RuntimeException("TeaVM compilation error");
|
||||
}
|
||||
|
||||
var disassemblyFile = new File(outputDir, "test.wast.html");
|
||||
try (var output = new PrintWriter(new OutputStreamWriter(new FileOutputStream(disassemblyFile)))) {
|
||||
var disassemblyWriter = new DisassemblyHTMLWriter(output);
|
||||
disassemblyWriter.setWithAddress(true);
|
||||
var disassembler = new Disassembler(disassemblyWriter);
|
||||
disassembler.disassemble(Files.readAllBytes(new File(outputDir, "test.wasm").toPath()));
|
||||
}
|
||||
|
||||
var testRunnerFile = new File(outputDir, "runner.js");
|
||||
try (var writer = new OutputStreamWriter(new FileOutputStream(testRunnerFile), StandardCharsets.UTF_8)) {
|
||||
writer.write("import { test } from '/resources/org/teavm/jso/export/" + name + ".js';\n");
|
||||
writer.write("export function main(args, callback) {\n");
|
||||
writer.write(" test().then(() => callback()).catch(e => callback(e));\n");
|
||||
writer.write("}\n");
|
||||
}
|
||||
var testProviderFile = new File(outputDir, "provider.js");
|
||||
try (var writer = new OutputStreamWriter(new FileOutputStream(testProviderFile), StandardCharsets.UTF_8)) {
|
||||
writer.write("await import('/resources/org/teavm/backend/wasm/wasm-gc-runtime.js');\n");
|
||||
writer.write("let teavm = await TeaVM.wasmGC.load('/tests/" + name + "/test.wasm');\n");
|
||||
writer.write("export default teavm.exports;\n");
|
||||
}
|
||||
|
||||
var descriptor = new BrowserRunDescriptor(name, "tests/" + name + "/runner.js", true,
|
||||
List.of("resources/org/teavm/jso/export/assert.js"), null);
|
||||
wasmGCRunner.runTest(descriptor);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createObject, consumeObject, C } from '/tests/exportClassMembers/test.js';
|
||||
const { createObject, consumeObject, C } = await (await import('/tests/exportClassMembers/provider.js')).default;
|
||||
|
||||
export async function test() {
|
||||
let o = createObject("qwe");
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { A, BB } from '/tests/exportClasses/test.js';
|
||||
const { A, BB } = await (await import("/tests/exportClasses/provider.js")).default;
|
||||
|
||||
export async function test() {
|
||||
assertEquals(23, A.foo());
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { takeObject } from '/tests/importClassMembers/test.js';
|
||||
const { takeObject } = await (await import('/tests/importClassMembers/provider.js')).default;
|
||||
|
||||
export async function test() {
|
||||
assertEquals("object taken: foo = 23, bar = qw", takeObject({
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { foo, bar, getCount, getAnotherCount } from '/tests/initializer/test.js';
|
||||
const { foo, bar, getCount, getAnotherCount } = await (await import('/tests/initializer/provider.js')).default;
|
||||
|
||||
export async function test() {
|
||||
assertEquals("foo", foo());
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import * as java from '/tests/primitives/test.js';
|
||||
const java = await (await import('/tests/primitives/provider.js')).default;
|
||||
|
||||
function testReturnPrimitives() {
|
||||
assertEquals(true, java.boolResult());
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { foo } from '/tests/simple/test.js';
|
||||
const { foo } = await (await import('/tests/simple/provider.js')).default;
|
||||
|
||||
export async function test() {
|
||||
assertEquals(23, foo());
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as varargs from '/tests/varargs/test.js';
|
||||
const varargs = await (await import('/tests/varargs/provider.js')).default;
|
||||
|
||||
export async function test() {
|
||||
assertEquals("strings: a, b", varargs.strings("a", "b"));
|
||||
|
|
|
@ -210,9 +210,7 @@ function launchWasmGCTest(file, argument, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
let instance = null;
|
||||
|
||||
TeaVM.wasm.load(file.path, {
|
||||
TeaVM.wasmGC.load(file.path, {
|
||||
installImports: function(o) {
|
||||
o.teavmConsole.putcharStdout = putchar;
|
||||
o.teavmConsole.putcharStderr = putcharStderr;
|
||||
|
@ -220,22 +218,16 @@ function launchWasmGCTest(file, argument, callback) {
|
|||
success() {
|
||||
callback(wrapResponse({ status: "OK" }));
|
||||
},
|
||||
failure(javaString) {
|
||||
let jsString = "";
|
||||
let length = instance.exports.stringLength(javaString);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
jsString += String.fromCharCode(instance.exports.charAt(javaString, i));
|
||||
}
|
||||
failure(message) {
|
||||
callback(wrapResponse({
|
||||
status: "failed",
|
||||
errorMessage: jsString
|
||||
errorMessage: message
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
}).then(teavm => {
|
||||
instance = teavm.instance;
|
||||
return teavm.main(argument ? [argument] : []);
|
||||
return teavm.exports.main(argument ? [argument] : []);
|
||||
}).catch(err => {
|
||||
callback(wrapResponse({
|
||||
status: "failed",
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.junit;
|
|||
|
||||
import org.teavm.classlib.impl.console.JSConsoleStringPrintStream;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.jso.core.JSString;
|
||||
|
||||
final class TestWasmGCEntryPoint {
|
||||
private TestWasmGCEntryPoint() {
|
||||
|
@ -29,7 +30,7 @@ final class TestWasmGCEntryPoint {
|
|||
} catch (Throwable e) {
|
||||
var out = new JSConsoleStringPrintStream();
|
||||
e.printStackTrace(out);
|
||||
reportFailure(out.toString());
|
||||
reportFailure(JSString.valueOf(out.toString()));
|
||||
}
|
||||
TestEntryPoint.run(args.length > 0 ? args[0] : null);
|
||||
}
|
||||
|
@ -38,5 +39,5 @@ final class TestWasmGCEntryPoint {
|
|||
private static native void reportSuccess();
|
||||
|
||||
@Import(module = "teavmTest", name = "failure")
|
||||
private static native void reportFailure(String message);
|
||||
private static native void reportFailure(JSString message);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<script type="text/javascript" src="${SCRIPT}-runtime.js"></script>
|
||||
<script type="text/javascript">
|
||||
let instance;
|
||||
TeaVM.wasm.load("${SCRIPT}", {
|
||||
TeaVM.wasmGC.load("${SCRIPT}", {
|
||||
installImports(o) {
|
||||
o.teavmTest = {
|
||||
success() {
|
||||
|
@ -32,22 +32,14 @@
|
|||
document.body.appendChild(pre);
|
||||
pre.appendChild(document.createTextNode("OK"));
|
||||
},
|
||||
failure(javaString) {
|
||||
let jsString = "";
|
||||
let length = instance.exports.stringLength(javaString);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
jsString += String.fromCharCode(instance.exports.charAt(javaString, i));
|
||||
}
|
||||
var pre = document.createElement("pre");
|
||||
failure(message) {
|
||||
let pre = document.createElement("pre");
|
||||
document.body.appendChild(pre);
|
||||
pre.appendChild(document.createTextNode(jsString));
|
||||
pre.appendChild(document.createTextNode(message));
|
||||
}
|
||||
};
|
||||
}
|
||||
}).then(teavm => {
|
||||
instance = teavm.instance;
|
||||
teavm.main(["${IDENTIFIER}"])
|
||||
});
|
||||
}).then(teavm => teavm.exports.main(["${IDENTIFIER}"]));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user