wasm gc: implement simple features from JSO

This commit is contained in:
Alexey Andreev 2024-09-27 20:28:11 +02:00
parent 33f4537f43
commit bbf45760b8
29 changed files with 986 additions and 52 deletions

View File

@ -15,6 +15,7 @@
*/
package org.teavm.backend.wasm.generate.gc;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
@ -46,6 +47,7 @@ public class WasmGCDeclarationsGenerator {
public final WasmFunctionTypes functionTypes;
private final WasmGCClassGenerator classGenerator;
private final WasmGCMethodGenerator methodGenerator;
private List<WasmGCInitializerContributor> initializerContributors = new ArrayList<>();
public WasmGCDeclarationsGenerator(
WasmModule module,
@ -77,7 +79,8 @@ public class WasmGCDeclarationsGenerator {
diagnostics,
customGenerators,
intrinsics,
strict
strict,
initializerContributors::add
);
var tags = new TagRegistry(classes, hierarchy);
var metadataRequirements = new ClassMetadataRequirements(dependencyInfo);
@ -132,7 +135,8 @@ public class WasmGCDeclarationsGenerator {
}
public void contributeToInitializer(WasmFunction function) {
var contributors = List.of(classGenerator, classGenerator.strings);
var contributors = new ArrayList<>(List.of(classGenerator, classGenerator.strings));
contributors.addAll(initializerContributors);
for (var contributor : contributors) {
contributor.contributeToInitializerDefinitions(function);
}

View File

@ -21,10 +21,12 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
@ -63,6 +65,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private Map<String, Set<String>> interfaceImplementors;
private WasmGCNameProvider names;
private boolean strict;
private Consumer<WasmGCInitializerContributor> initializerContributors;
public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
@ -70,7 +73,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider,
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings,
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics,
WasmGCNameProvider names, boolean strict) {
WasmGCNameProvider names, boolean strict, Consumer<WasmGCInitializerContributor> initializerContributors) {
this.module = module;
this.virtualTables = virtualTables;
this.typeMapper = typeMapper;
@ -87,6 +90,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
this.intrinsics = intrinsics;
this.names = names;
this.strict = strict;
this.initializerContributors = initializerContributors;
}
public WasmGCClassInfoProvider classInfoProvider() {
@ -222,4 +226,17 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
}
}
}
public void addToInitializer(Consumer<WasmFunction> initializer) {
initializerContributors.accept(new WasmGCInitializerContributor() {
@Override
public void contributeToInitializerDefinitions(WasmFunction function) {
}
@Override
public void contributeToInitializer(WasmFunction function) {
initializer.accept(function);
}
});
}
}

View File

@ -17,6 +17,7 @@ package org.teavm.backend.wasm.generate.gc.methods;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.teavm.ast.ArrayFromDataExpr;
import org.teavm.ast.ArrayType;
@ -816,5 +817,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
public WasmTag exceptionTag() {
return context.getExceptionTag();
}
@Override
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
context.addToInitializer(initializerContributor);
}
};
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.function.Consumer;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
@ -32,6 +33,7 @@ import org.teavm.backend.wasm.gc.PreciseTypeInference;
import org.teavm.backend.wasm.gc.PreciseValueType;
import org.teavm.backend.wasm.gc.WasmGCVariableCategoryProvider;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
@ -86,6 +88,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
private WasmGCStandardClasses standardClasses;
private WasmGCStringProvider strings;
private boolean strict;
private Consumer<WasmGCInitializerContributor> initializerContributors;
public WasmGCMethodGenerator(
WasmModule module,
@ -99,7 +102,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
Diagnostics diagnostics,
WasmGCCustomGeneratorProvider customGenerators,
WasmGCIntrinsicProvider intrinsics,
boolean strict
boolean strict,
Consumer<WasmGCInitializerContributor> initializerContributors
) {
this.module = module;
this.hierarchy = hierarchy;
@ -113,6 +117,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
this.customGenerators = customGenerators;
this.intrinsics = intrinsics;
this.strict = strict;
this.initializerContributors = initializerContributors;
}
public void setTypeMapper(WasmGCTypeMapper typeMapper) {
@ -356,7 +361,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
customGenerators,
intrinsics,
names,
strict
strict,
initializerContributors
);
}
return context;

View File

@ -15,6 +15,7 @@
*/
package org.teavm.backend.wasm.intrinsics.gc;
import java.util.function.Consumer;
import org.teavm.ast.Expr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
@ -25,6 +26,7 @@ import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
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.backend.wasm.model.expression.WasmExpression;
@ -58,4 +60,6 @@ public interface WasmGCIntrinsicContext {
ClassLoader classLoader();
WasmTag exceptionTag();
void addToInitializer(Consumer<WasmFunction> initializerContributor);
}

View File

@ -17,6 +17,9 @@
var TeaVM = TeaVM || {};
TeaVM.wasm = function() {
let exports;
let getGlobalName = function(name) {
return eval("return " + {name});
}
function defaults(imports) {
let stderr = "";
let stdout = "";
@ -92,18 +95,51 @@ TeaVM.wasm = function() {
}
return weakRef;
},
deref(weakRef) {
return weakRef.deref();
},
deref: weakRef => weakRef.deref(),
createStringWeakRef(value, heldValue) {
let weakRef = new WeakRef(value);
stringFinalizationRegistry.register(value, heldValue)
return weakRef;
},
stringDeref(weakRef) {
return weakRef.deref();
}
stringDeref: weakRef => weakRef.deref()
};
function identity(value) {
return value;
}
imports.teavmJso = {
emptyString: () => "",
stringFromCharCode: code => String.fromCharCode(code),
concatStrings: (a, b) => a + b,
stringLength: s => s.length,
charAt: (s, index) => s.charCodeAt(index),
emptyArray: () => [],
appendToArray: (array, e) => array.push(e),
unwrapBoolean: value => value ? 1 : 0,
wrapBoolean: value => !!value,
getProperty: (obj, prop) => obj[prop],
getPropertyPure: (obj, prop) => obj[prop],
setProperty: (obj, prop, value) => obj[prop] = value,
setPropertyPure: (obj, prop) => obj[prop] = value,
global: getGlobalName
};
for (let name of ["wrapByte", "wrapShort", "wrapChar", "wrapInt", "wrapFloat", "wrapDouble", "unwrapByte",
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
imports.teavmJso[name] = identity;
}
for (let i = 0; i < 32; ++i) {
imports.teavmJso["createFunction" + i] = function() {
return new Function(...arguments);
};
imports.teavmJso["callFunction" + i] = function(fn, ...args) {
return fn(...args);
};
imports.teavmJso["callMethod" + i] = function(instance, method, ...args) {
return instance[method](...args);
};
imports.teavmJso["construct" + i] = function(constructor, ...args) {
return new constructor(...args);
};
}
imports.teavmMath = Math;
}

View File

@ -19,6 +19,7 @@ import java.lang.reflect.Array;
import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
@ -28,7 +29,7 @@ import org.teavm.jso.core.JSBoolean;
import org.teavm.jso.core.JSNumber;
import org.teavm.jso.core.JSString;
final class JS {
public final class JS {
private JS() {
}
@ -77,30 +78,37 @@ final class JS {
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapByte", module = "teavmJso")
public static native JSObject wrap(byte value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapShort", module = "teavmJso")
public static native JSObject wrap(short value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapInt", module = "teavmJso")
public static native JSObject wrap(int value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapChar", module = "teavmJso")
public static native JSObject wrap(char value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapFloat", module = "teavmJso")
public static native JSObject wrap(float value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapDouble", module = "teavmJso")
public static native JSObject wrap(double value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "wrapBoolean", module = "teavmJso")
public static native JSObject wrap(boolean value);
@InjectedBy(JSNativeInjector.class)
@ -109,30 +117,37 @@ final class JS {
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapByte", module = "teavmJso")
public static native byte unwrapByte(JSObject value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapChar", module = "teavmJso")
public static native char unwrapCharacter(JSObject value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapShort", module = "teavmJso")
public static native short unwrapShort(JSObject value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapInt", module = "teavmJso")
public static native int unwrapInt(JSObject value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapFloat", module = "teavmJso")
public static native float unwrapFloat(JSObject value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapDouble", module = "teavmJso")
public static native double unwrapDouble(JSObject value);
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "unwrapBoolean", module = "teavmJso")
public static native boolean unwrapBoolean(JSObject value);
@InjectedBy(JSNativeInjector.class)
@ -466,68 +481,82 @@ final class JS {
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod0", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod1", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod2", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod3", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod4", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod5", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod6", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod7", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod8", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod9", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod10", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod11", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod12", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k,
JSObject l);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "callMethod13", module = "teavmJso")
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k,
JSObject l, JSObject m);
@ -608,85 +637,103 @@ final class JS {
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct0", module = "teavmJso")
public static native JSObject construct(JSObject cls);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct1", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct2", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct3", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct4", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct5", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct6", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct7", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct8", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g, JSObject h);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct9", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g, JSObject h, JSObject i);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct10", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g, JSObject h, JSObject i, JSObject j);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct11", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct12", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k, JSObject l);
@InjectedBy(JSNativeInjector.class)
@PluggableDependency(JSNativeInjector.class)
@Import(name = "construct13", module = "teavmJso")
public static native JSObject construct(JSObject cls, JSObject a, JSObject b, JSObject c, JSObject d, JSObject e,
JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k, JSObject l, JSObject m);
@InjectedBy(JSNativeInjector.class)
@JSBody(params = { "instance", "index" }, script = "return instance[index];")
@Import(name = "getProperty", module = "teavmJso")
public static native JSObject get(JSObject instance, JSObject index);
@InjectedBy(JSNativeInjector.class)
@JSBody(params = { "instance", "index" }, script = "return instance[index];")
@NoSideEffects
@Import(name = "getPropertyPure", module = "teavmJso")
public static native JSObject getPure(JSObject instance, JSObject index);
@InjectedBy(JSNativeInjector.class)
@JSBody(params = { "instance", "index", "obj" }, script = "instance[index] = obj;")
@Import(name = "setProperty", module = "teavmJso")
public static native void set(JSObject instance, JSObject index, JSObject obj);
@InjectedBy(JSNativeInjector.class)
@JSBody(params = { "instance", "index", "obj" }, script = "instance[index] = obj;")
@NoSideEffects
@Import(name = "setPropertyPure", module = "teavmJso")
public static native void setPure(JSObject instance, JSObject index, JSObject obj);
@GeneratedBy(JSNativeGenerator.class)
@ -699,6 +746,7 @@ final class JS {
@InjectedBy(JSNativeInjector.class)
@NoSideEffects
@Import(name = "global", module = "teavmJso")
public static native JSObject global(String name);
@InjectedBy(JSNativeInjector.class)

View File

@ -26,22 +26,39 @@ import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.model.MethodReference;
class JSBodyAstEmitter implements JSBodyEmitter {
public class JSBodyAstEmitter implements JSBodyEmitter {
private boolean isStatic;
private AstNode ast;
private AstNode rootAst;
private final MethodReference method;
public final AstNode ast;
public final AstNode rootAst;
private String[] parameterNames;
private JsBodyImportInfo[] imports;
JSBodyAstEmitter(boolean isStatic, AstNode ast, AstNode rootAst, String[] parameterNames,
JSBodyAstEmitter(boolean isStatic, MethodReference method, AstNode ast, AstNode rootAst, String[] parameterNames,
JsBodyImportInfo[] imports) {
this.isStatic = isStatic;
this.method = method;
this.ast = ast;
this.rootAst = rootAst;
this.parameterNames = parameterNames;
this.imports = imports;
}
@Override
public MethodReference method() {
return method;
}
@Override
public boolean isStatic() {
return isStatic;
}
@Override
public String[] parameterNames() {
return parameterNames.clone();
}
@Override
public void emit(InjectorContext context) {
var astWriter = new AstWriter(context.getWriter(), new DefaultGlobalNameWriter());

View File

@ -20,10 +20,10 @@ import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.model.MethodReference;
class JSBodyBloatedEmitter implements JSBodyEmitter {
public class JSBodyBloatedEmitter implements JSBodyEmitter {
private boolean isStatic;
private MethodReference method;
private String script;
public final String script;
private String[] parameterNames;
private JsBodyImportInfo[] imports;
@ -36,6 +36,21 @@ class JSBodyBloatedEmitter implements JSBodyEmitter {
this.imports = imports;
}
@Override
public MethodReference method() {
return method;
}
@Override
public boolean isStatic() {
return isStatic;
}
@Override
public String[] parameterNames() {
return parameterNames.clone();
}
@Override
public void emit(InjectorContext context) {
emit(context.getWriter(), new EmissionStrategy() {

View File

@ -20,8 +20,14 @@ import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.model.MethodReference;
interface JSBodyEmitter {
public interface JSBodyEmitter {
MethodReference method();
void emit(InjectorContext context);
void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef);
String[] parameterNames();
boolean isStatic();
}

View File

@ -21,12 +21,12 @@ import java.util.Map;
import java.util.Set;
import org.teavm.model.MethodReference;
class JSBodyRepository {
final Map<MethodReference, JSBodyEmitter> emitters = new HashMap<>();
final Map<MethodReference, JsBodyImportInfo[]> imports = new HashMap<>();
final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
final Set<MethodReference> processedMethods = new HashSet<>();
final Set<MethodReference> inlineMethods = new HashSet<>();
final Map<MethodReference, MethodReference> callbackCallees = new HashMap<>();
final Map<MethodReference, Set<MethodReference>> callbackMethods = new HashMap<>();
public class JSBodyRepository {
public final Map<MethodReference, JSBodyEmitter> emitters = new HashMap<>();
public final Map<MethodReference, JsBodyImportInfo[]> imports = new HashMap<>();
public final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
public final Set<MethodReference> processedMethods = new HashSet<>();
public final Set<MethodReference> inlineMethods = new HashSet<>();
public final Map<MethodReference, MethodReference> callbackCallees = new HashMap<>();
public final Map<MethodReference, Set<MethodReference>> callbackMethods = new HashMap<>();
}

View File

@ -227,7 +227,7 @@ class JSClassProcessor {
for (int i = 0; i < signature.length; ++i) {
staticSignature[i + 1] = signature[i];
}
staticSignature[0] = ValueType.object(method.getClassName());
staticSignature[0] = ValueType.object(JSObject.class.getName());
return staticSignature;
}
@ -1139,7 +1139,8 @@ class JSClassProcessor {
expr = body;
}
javaInvocationProcessor.process(location, expr);
var emitter = new JSBodyAstEmitter(isStatic, expr, rootNode, parameterNames, imports);
var emitter = new JSBodyAstEmitter(isStatic, methodToProcess.getReference(), expr, rootNode,
parameterNames, imports);
repository.emitters.put(proxyMethod, emitter);
}
if (imports.length > 0) {

View File

@ -16,8 +16,10 @@
package org.teavm.jso.impl;
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
import org.teavm.jso.JSExceptions;
import org.teavm.jso.JSObject;
import org.teavm.jso.impl.wasmgc.WasmGCJso;
import org.teavm.model.MethodReference;
import org.teavm.platform.plugin.PlatformPlugin;
import org.teavm.vm.TeaVMPluginUtil;
@ -29,19 +31,36 @@ import org.teavm.vm.spi.TeaVMPlugin;
public class JSOPlugin implements TeaVMPlugin {
@Override
public void install(TeaVMHost host) {
TeaVMJavaScriptHost jsHost = host.getExtension(TeaVMJavaScriptHost.class);
if (jsHost == null) {
return;
}
JSBodyRepository repository = new JSBodyRepository();
host.registerService(JSBodyRepository.class, repository);
host.add(new JSObjectClassTransformer(repository));
JSDependencyListener dependencyListener = new JSDependencyListener(repository);
JSAliasRenderer aliasRenderer = new JSAliasRenderer();
host.add(dependencyListener);
host.add(new JSExceptionsDependencyListener());
var wrapperDependency = new JSWrapperDependency();
host.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
wrapperDependency);
host.add(new MethodReference(JSWrapper.class, "dependencyJavaToJs", Object.class, JSObject.class),
wrapperDependency);
host.add(new MethodReference(JSWrapper.class, "dependencyJsToJava", JSObject.class, Object.class),
wrapperDependency);
TeaVMPluginUtil.handleNatives(host, JS.class);
var jsHost = host.getExtension(TeaVMJavaScriptHost.class);
if (jsHost != null) {
installForJS(jsHost);
}
var wasmGCHost = host.getExtension(TeaVMWasmGCHost.class);
if (wasmGCHost != null) {
WasmGCJso.install(host, wasmGCHost, repository);
}
}
private void installForJS(TeaVMJavaScriptHost jsHost) {
var aliasRenderer = new JSAliasRenderer();
jsHost.add(aliasRenderer);
jsHost.addGeneratorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(),
DynamicGenerator.class.getName()));
@ -50,7 +69,7 @@ public class JSOPlugin implements TeaVMPlugin {
jsHost.addVirtualMethods(aliasRenderer);
jsHost.addForcedFunctionMethods(new JSExportedMethodAsFunction());
JSExceptionsGenerator exceptionsGenerator = new JSExceptionsGenerator();
var exceptionsGenerator = new JSExceptionsGenerator();
jsHost.add(new MethodReference(JSExceptions.class, "getJavaException", JSObject.class, Throwable.class),
exceptionsGenerator);
jsHost.add(new MethodReference(JSExceptions.class, "getJSException", Throwable.class, JSObject.class),
@ -75,14 +94,5 @@ public class JSOPlugin implements TeaVMPlugin {
wrapperGenerator);
jsHost.add(new MethodReference(JSWrapper.class, "isJSImplementation", Object.class, boolean.class),
wrapperGenerator);
host.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
wrapperGenerator);
host.add(new MethodReference(JSWrapper.class, "dependencyJavaToJs", Object.class, JSObject.class),
wrapperGenerator);
host.add(new MethodReference(JSWrapper.class, "dependencyJsToJava", JSObject.class, Object.class),
wrapperGenerator);
TeaVMPluginUtil.handleNatives(host, JS.class);
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2023 konsoletyper.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.jso.impl;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyNode;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
public class JSWrapperDependency implements DependencyPlugin {
private DependencyNode externalClassesNode;
@Override
public void methodReached(DependencyAgent agent, MethodDependency method) {
switch (method.getMethod().getName()) {
case "jsToWrapper":
method.getResult().propagate(agent.getType(JSWrapper.class.getName()));
break;
case "dependencyJavaToJs":
method.getVariable(1).connect(getExternalClassesNode(agent));
break;
case "dependencyJsToJava":
getExternalClassesNode(agent).connect(method.getResult());
break;
}
}
private DependencyNode getExternalClassesNode(DependencyAgent agent) {
if (externalClassesNode == null) {
externalClassesNode = agent.createNode();
}
return externalClassesNode;
}
}

View File

@ -15,11 +15,11 @@
*/
package org.teavm.jso.impl;
class JsBodyImportInfo {
final String alias;
final String fromModule;
public class JsBodyImportInfo {
public final String alias;
public final String fromModule;
JsBodyImportInfo(String alias, String fromModule) {
public JsBodyImportInfo(String alias, String fromModule) {
this.alias = alias;
this.fromModule = fromModule;
}

View File

@ -0,0 +1,108 @@
/*
* 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.wasmgc;
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.teavm.backend.javascript.rendering.AstWriter;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.jso.impl.JSBodyAstEmitter;
import org.teavm.jso.impl.JSBodyBloatedEmitter;
import org.teavm.jso.impl.JSBodyEmitter;
class WasmGCBodyGenerator {
private WasmGCJSFunctions jsFunctions;
private boolean initialized;
private List<Consumer<WasmFunction>> initializerParts = new ArrayList<>();
WasmGCBodyGenerator(WasmGCJSFunctions jsFunctions) {
this.jsFunctions = jsFunctions;
}
private void initialize(WasmGCIntrinsicContext context) {
if (initialized) {
return;
}
initialized = true;
context.addToInitializer(this::writeToInitializer);
}
private void writeToInitializer(WasmFunction function) {
for (var part : initializerParts) {
part.accept(function);
}
}
WasmGlobal addBody(WasmGCIntrinsicContext context, JSBodyEmitter emitter, boolean inlined) {
initialize(context);
var paramCount = emitter.method().parameterCount();
if (!emitter.isStatic()) {
paramCount++;
}
var global = new WasmGlobal(context.names().suggestForMethod(emitter.method()),
WasmType.Reference.EXTERN, new WasmNullConstant(WasmType.Reference.EXTERN));
context.module().globals.add(global);
var body = "";
if (emitter instanceof JSBodyBloatedEmitter) {
body = ((JSBodyBloatedEmitter) emitter).script;
} else if (emitter instanceof JSBodyAstEmitter) {
var writer = new WasmGCJSBodyWriter();
if (inlined) {
writer.sb.append("return ");
}
var astEmitter = (JSBodyAstEmitter) emitter;
var astWriter = new AstWriter(writer, name -> (w, prec) -> w.append(name));
if (!emitter.isStatic()) {
astWriter.declareNameEmitter("this", (w, prec) -> w.append("__this__"));
}
astWriter.print(astEmitter.ast);
if (inlined) {
writer.sb.append(";");
}
body = writer.sb.toString();
} else {
throw new IllegalArgumentException();
}
var constructor = new WasmCall(jsFunctions.getFunctionConstructor(context,
paramCount));
var stringToJs = context.functions().forStaticMethod(STRING_TO_JS);
var paramNames = new ArrayList<String>();
if (!emitter.isStatic()) {
paramNames.add("__this__");
}
paramNames.addAll(List.of(emitter.parameterNames()));
for (var parameter : paramNames) {
var paramName = new WasmGetGlobal(context.strings().getStringConstant(parameter).global);
constructor.getArguments().add(new WasmCall(stringToJs, paramName));
}
var functionBody = new WasmGetGlobal(context.strings().getStringConstant(body).global);
constructor.getArguments().add(new WasmCall(stringToJs, functionBody));
initializerParts.add(initializer -> initializer.getBody().add(new WasmSetGlobal(global, constructor)));
return global;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.wasmgc;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.jso.impl.JSBodyEmitter;
class WasmGCBodyIntrinsic implements WasmGCIntrinsic {
private JSBodyEmitter emitter;
private boolean inlined;
private WasmGCBodyGenerator bodyGenerator;
private WasmGlobal global;
private WasmGCJSFunctions jsFunctions;
WasmGCBodyIntrinsic(JSBodyEmitter emitter, boolean inlined, WasmGCBodyGenerator bodyGenerator,
WasmGCJSFunctions jsFunctions) {
this.emitter = emitter;
this.inlined = inlined;
this.bodyGenerator = bodyGenerator;
this.jsFunctions = jsFunctions;
}
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
if (global == null) {
global = bodyGenerator.addBody(context, emitter, inlined);
}
var call = new WasmCall(jsFunctions.getFunctionCaller(context, invocation.getArguments().size()));
call.getArguments().add(new WasmGetGlobal(global));
for (var arg : invocation.getArguments()) {
call.getArguments().add(context.generate(arg));
}
return call;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.wasmgc;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactoryContext;
import org.teavm.jso.impl.JSBodyRepository;
import org.teavm.model.MethodReference;
class WasmGCJSBodyRenderer implements WasmGCIntrinsicFactory {
private JSBodyRepository repository;
private WasmGCJSFunctions jsFunctions;
private WasmGCBodyGenerator bodyGenerator;
WasmGCJSBodyRenderer(JSBodyRepository repository) {
this.repository = repository;
jsFunctions = new WasmGCJSFunctions();
bodyGenerator = new WasmGCBodyGenerator(jsFunctions);
}
@Override
public WasmGCIntrinsic createIntrinsic(MethodReference methodRef, WasmGCIntrinsicFactoryContext context) {
var emitter = repository.emitters.get(methodRef);
if (emitter == null) {
return null;
}
var inlined = repository.inlineMethods.contains(emitter.method());
return new WasmGCBodyIntrinsic(emitter, inlined, bodyGenerator, jsFunctions);
}
}

View File

@ -0,0 +1,188 @@
/*
* 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.wasmgc;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
class WasmGCJSBodyWriter extends SourceWriter {
final StringBuilder sb = new StringBuilder();
@Override
public SourceWriter append(char value) {
sb.append(value);
return this;
}
@Override
public SourceWriter append(CharSequence csq, int start, int end) {
sb.append(csq, start, end);
return this;
}
@Override
public SourceWriter appendClass(String cls) {
return this;
}
@Override
public SourceWriter appendField(FieldReference field) {
return this;
}
@Override
public SourceWriter appendStaticField(FieldReference field) {
return this;
}
@Override
public SourceWriter appendVirtualMethod(MethodDescriptor method) {
return this;
}
@Override
public SourceWriter appendMethod(MethodReference method) {
return this;
}
@Override
public SourceWriter appendFunction(String name) {
return this;
}
@Override
public SourceWriter startFunctionDeclaration() {
return this;
}
@Override
public SourceWriter startVariableDeclaration() {
return this;
}
@Override
public SourceWriter endDeclaration() {
return this;
}
@Override
public SourceWriter declareVariable() {
return this;
}
@Override
public SourceWriter appendGlobal(String name) {
sb.append(name);
return this;
}
@Override
public SourceWriter appendInit(MethodReference method) {
return this;
}
@Override
public SourceWriter appendClassInit(String className) {
return this;
}
@Override
public SourceWriter newLine() {
sb.append('\n');
return this;
}
@Override
public SourceWriter ws() {
return this;
}
@Override
public SourceWriter sameLineWs() {
return this;
}
@Override
public SourceWriter tokenBoundary() {
return this;
}
@Override
public SourceWriter softNewLine() {
return this;
}
@Override
public SourceWriter indent() {
return this;
}
@Override
public SourceWriter outdent() {
return this;
}
@Override
public SourceWriter emitLocation(String fileName, int line) {
return this;
}
@Override
public SourceWriter enterLocation() {
return this;
}
@Override
public SourceWriter exitLocation() {
return this;
}
@Override
public SourceWriter emitStatementStart() {
return this;
}
@Override
public SourceWriter emitVariables(String[] names, String jsName) {
return this;
}
@Override
public void emitMethod(MethodDescriptor method) {
}
@Override
public void emitClass(String className) {
}
@Override
public void markClassStart(String className) {
}
@Override
public void markClassEnd() {
}
@Override
public void markSectionStart(int id) {
}
@Override
public void markSectionEnd() {
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.wasmgc;
import org.teavm.jso.JSObject;
import org.teavm.model.MethodReference;
final class WasmGCJSConstants {
private WasmGCJSConstants() {
}
static final MethodReference STRING_TO_JS = new MethodReference(WasmGCJSRuntime.class,
"stringToJs", String.class, JSObject.class);
static final MethodReference JS_TO_STRING = new MethodReference(WasmGCJSRuntime.class,
"jsToString", JSObject.class, String.class);
}

View File

@ -0,0 +1,34 @@
/*
* 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.wasmgc;
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.JS_TO_STRING;
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
import org.teavm.dependency.AbstractDependencyListener;
import org.teavm.dependency.DependencyAgent;
class WasmGCJSDependencies extends AbstractDependencyListener {
@Override
public void started(DependencyAgent agent) {
agent.linkMethod(STRING_TO_JS)
.propagate(1, agent.getType("java.lang.String"))
.use();
var jsToString = agent.linkMethod(JS_TO_STRING);
jsToString.getResult().propagate(agent.getType("java.lang.String"));
jsToString.use();
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.wasmgc;
import java.util.Arrays;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmType;
class WasmGCJSFunctions {
private WasmFunction[] constructors = new WasmFunction[32];
private WasmFunction[] callers = new WasmFunction[32];
WasmFunction getFunctionConstructor(WasmGCIntrinsicContext context, int index) {
var function = constructors[index];
if (function == null) {
var extern = WasmType.SpecialReferenceKind.EXTERN.asNonNullType();
var constructorParamTypes = new WasmType[index + 1];
Arrays.fill(constructorParamTypes, extern);
var functionType = context.functionTypes().of(extern, constructorParamTypes);
function = new WasmFunction(functionType);
function.setName(context.names().topLevel("teavm.js:createFunction" + index));
function.setImportModule("teavmJso");
function.setImportName("createFunction" + index);
context.module().functions.add(function);
constructors[index] = function;
}
return function;
}
WasmFunction getFunctionCaller(WasmGCIntrinsicContext context, int index) {
var function = callers[index];
if (function == null) {
var extern = WasmType.SpecialReferenceKind.EXTERN.asNonNullType();
var paramTypes = new WasmType[index + 1];
Arrays.fill(paramTypes, extern);
paramTypes[0] = WasmType.Reference.EXTERN;
var functionType = context.functionTypes().of(extern, paramTypes);
function = new WasmFunction(functionType);
function.setName(context.names().topLevel("teavm.js:callFunction" + index));
function.setImportModule("teavmJso");
function.setImportName("callFunction" + index);
context.module().functions.add(function);
callers[index] = function;
}
return function;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.wasmgc;
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.JS_TO_STRING;
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression;
class WasmGCJSIntrinsic implements WasmGCIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
switch (invocation.getMethod().getName()) {
case "wrap": {
var function = context.functions().forStaticMethod(STRING_TO_JS);
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
}
case "unwrapString": {
var function = context.functions().forStaticMethod(JS_TO_STRING);
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
}
default:
throw new IllegalArgumentException();
}
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.wasmgc;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
final class WasmGCJSRuntime {
private WasmGCJSRuntime() {
}
static JSObject stringToJs(String str) {
if (str.isEmpty()) {
return emptyString();
}
var jsStr = stringFromCharCode(str.charAt(0));
for (var i = 1; i < str.length(); ++i) {
jsStr = concatStrings(jsStr, stringFromCharCode(str.charAt(i)));
}
return jsStr;
}
static String jsToString(JSObject obj) {
var length = stringLength(obj);
if (length == 0) {
return "";
}
var chars = new char[length];
for (var i = 0; i < length; ++i) {
chars[i] = charAt(obj, i);
}
return new String(chars);
}
@Import(name = "emptyString", module = "teavmJso")
static native JSObject emptyString();
@Import(name = "stringFromCharCode", module = "teavmJso")
static native JSObject stringFromCharCode(char c);
@Import(name = "concatStrings", module = "teavmJso")
static native JSObject concatStrings(JSObject a, JSObject b);
@Import(name = "emptyArray", module = "teavmJso")
static native JSObject emptyArray();
@Import(name = "appendToArray", module = "teavmJso")
static native JSObject appendToArray(JSObject array, JSObject element);
@Import(name = "stringLength", module = "teavmJso")
static native int stringLength(JSObject str);
@Import(name = "charAt", module = "teavmJso")
static native char charAt(JSObject str, int index);
}

View File

@ -0,0 +1,37 @@
/*
* 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.wasmgc;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapper;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactoryContext;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.jso.JSObject;
class WasmGCJSTypeMapper implements WasmGCCustomTypeMapper, WasmGCCustomTypeMapperFactory {
@Override
public WasmType map(String className) {
if (className.equals(JSObject.class.getName())) {
return WasmType.SpecialReferenceKind.EXTERN.asNonNullType();
}
return null;
}
@Override
public WasmGCCustomTypeMapper createTypeMapper(WasmGCCustomTypeMapperFactoryContext context) {
return this;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.wasmgc;
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.model.MethodReference;
import org.teavm.vm.spi.TeaVMHost;
public final class WasmGCJso {
private WasmGCJso() {
}
public static void install(TeaVMHost host, TeaVMWasmGCHost wasmGCHost, JSBodyRepository jsBodyRepository) {
host.add(new WasmGCJSDependencies());
wasmGCHost.addCustomTypeMapperFactory(new WasmGCJSTypeMapper());
wasmGCHost.addIntrinsicFactory(new WasmGCJSBodyRenderer(jsBodyRepository));
var jsIntrinsic = new WasmGCJSIntrinsic();
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "wrap", String.class, JSObject.class), jsIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "unwrapString", JSObject.class, String.class),
jsIntrinsic);
}
}

View File

@ -30,7 +30,7 @@ import org.teavm.junit.TestPlatform;
@RunWith(TeaVMTestRunner.class)
@SkipJVM
@OnlyPlatform(TestPlatform.JAVASCRIPT)
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
@EachTestCompiledSeparately
public class AnnotationsTest {
@Test

View File

@ -29,12 +29,13 @@ import org.teavm.jso.core.JSString;
import org.teavm.junit.EachTestCompiledSeparately;
import org.teavm.junit.OnlyPlatform;
import org.teavm.junit.SkipJVM;
import org.teavm.junit.SkipPlatform;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform;
@RunWith(TeaVMTestRunner.class)
@SkipJVM
@OnlyPlatform(TestPlatform.JAVASCRIPT)
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
@EachTestCompiledSeparately
public class ConversionTest {
@Test
@ -57,6 +58,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsPrimitiveArraysToJavaScript() {
assertEquals("true:2:3:64:4:5.5:6.5:foo", combinePrimitiveArrays(new boolean[] { true }, new byte[] { 2 },
new short[] { 3 }, new char[] { '@' }, new int[] { 4 }, new float[] { 5.5F }, new double[] { 6.5 },
@ -64,6 +66,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsPrimitiveArraysToJava() {
PrimitiveArrays arrays = getPrimitiveArrays();
@ -81,6 +84,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsPrimitiveArrays2ToJavaScript() {
assertEquals("true:2:3:64:4:5.5:6.5:foo", combinePrimitiveArrays2(new boolean[][] {{ true }},
new byte[][] {{ 2 }}, new short[][] {{ 3 }}, new char[][] {{ '@' }}, new int[][] {{ 4 }},
@ -88,6 +92,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsPrimitiveArrays2ToJava() {
PrimitiveArrays2 arrays = getPrimitiveArrays2();
@ -106,6 +111,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsPrimitiveArrays4ToJavaScript() {
assertEquals("true:2:3:64:4:5.5:6.5:foo", combinePrimitiveArrays4(new boolean[][][][] {{{{ true }}}},
new byte[][][][] {{{{ 2 }}}}, new short[][][][] {{{{ 3 }}}}, new char[][][][] {{{{ '@' }}}},
@ -114,6 +120,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsPrimitiveArrays4ToJava() {
PrimitiveArrays4 arrays = getPrimitiveArrays4();
@ -137,6 +144,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void convertsArrayOfJSObject() {
assertEquals("(foo)", surround(new JSString[] { JSString.valueOf("foo") })[0].stringValue());
assertEquals("(foo)", surround(new JSString[][] {{ JSString.valueOf("foo") }})[0][0].stringValue());
@ -145,6 +153,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void copiesArray() {
int[] array = { 23 };
assertEquals(24, mutate(array));
@ -152,6 +161,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void passesArrayByRef() {
int[] array = { 23, 42 };
@ -165,6 +175,7 @@ public class ConversionTest {
}
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void returnsArrayByRef() {
int[] first = { 23, 42 };
int[] second = rewrap(first);

View File

@ -23,12 +23,13 @@ import org.teavm.jso.JSObject;
import org.teavm.junit.EachTestCompiledSeparately;
import org.teavm.junit.OnlyPlatform;
import org.teavm.junit.SkipJVM;
import org.teavm.junit.SkipPlatform;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform;
@RunWith(TeaVMTestRunner.class)
@SkipJVM
@OnlyPlatform(TestPlatform.JAVASCRIPT)
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
@EachTestCompiledSeparately
public class ImplementationTest {
@Test
@ -44,6 +45,7 @@ public class ImplementationTest {
static native int mul(int a, int b);
@Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void inliningUsageCounterWorksProperly() {
ForInliningTest instance = ForInliningTest.create();
wrongInlineCandidate(instance.foo());