mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: support exporting Java classes to JavaScript
This commit is contained in:
parent
0897a1bbd5
commit
383fee67c5
|
@ -77,6 +77,7 @@ public class Renderer implements RenderingManager {
|
|||
|
||||
private final SourceWriter writer;
|
||||
private final ListableClassReaderSource classSource;
|
||||
private final ClassReaderSource originalClassSource;
|
||||
private final ClassLoader classLoader;
|
||||
private final Properties properties = new Properties();
|
||||
private final ServiceRepository services;
|
||||
|
@ -103,6 +104,7 @@ public class Renderer implements RenderingManager {
|
|||
List<ExportedDeclaration> exports, String entryPoint) {
|
||||
this.writer = writer;
|
||||
this.classSource = context.getClassSource();
|
||||
this.originalClassSource = context.getInitialClassSource();
|
||||
this.classLoader = context.getClassLoader();
|
||||
this.services = context.getServices();
|
||||
this.asyncMethods = new HashSet<>(asyncMethods);
|
||||
|
@ -152,6 +154,11 @@ public class Renderer implements RenderingManager {
|
|||
return classSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassReaderSource getOriginalClassSource() {
|
||||
return originalClassSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.javascript.rendering;
|
|||
import java.util.Properties;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
|
@ -32,6 +33,8 @@ public interface RenderingManager extends ServiceRepository {
|
|||
|
||||
ListableClassReaderSource getClassSource();
|
||||
|
||||
ClassReaderSource getOriginalClassSource();
|
||||
|
||||
ClassLoader getClassLoader();
|
||||
|
||||
Properties getProperties();
|
||||
|
|
|
@ -175,6 +175,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
var declarationsGenerator = new WasmGCDeclarationsGenerator(
|
||||
module,
|
||||
classes,
|
||||
controller.getUnprocessedClassSource(),
|
||||
controller.getClassLoader(),
|
||||
controller.getClassInitializerInfo(),
|
||||
controller.getDependencyInfo(),
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.teavm.backend.wasm.model.WasmModule;
|
|||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ListableClassHolderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||
|
@ -52,6 +53,7 @@ public class WasmGCDeclarationsGenerator {
|
|||
public WasmGCDeclarationsGenerator(
|
||||
WasmModule module,
|
||||
ListableClassHolderSource classes,
|
||||
ClassReaderSource originalClasses,
|
||||
ClassLoader classLoader,
|
||||
ClassInitializerInfo classInitializerInfo,
|
||||
DependencyInfo dependencyInfo,
|
||||
|
@ -87,6 +89,7 @@ public class WasmGCDeclarationsGenerator {
|
|||
classGenerator = new WasmGCClassGenerator(
|
||||
module,
|
||||
classes,
|
||||
originalClasses,
|
||||
hierarchy,
|
||||
dependencyInfo,
|
||||
functionTypes,
|
||||
|
|
|
@ -102,6 +102,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
|
||||
private final WasmModule module;
|
||||
private ClassReaderSource classSource;
|
||||
private ClassReaderSource originalClassSource;
|
||||
private ClassHierarchy hierarchy;
|
||||
private WasmFunctionTypes functionTypes;
|
||||
private TagRegistry tagRegistry;
|
||||
|
@ -158,7 +159,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
private boolean hasLoadServices;
|
||||
|
||||
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
|
||||
ClassHierarchy hierarchy, DependencyInfo dependencyInfo,
|
||||
ClassReaderSource originalClassSource, ClassHierarchy hierarchy, DependencyInfo dependencyInfo,
|
||||
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
|
||||
ClassMetadataRequirements metadataRequirements, WasmGCVirtualTableProvider virtualTables,
|
||||
BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names,
|
||||
|
@ -166,6 +167,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories) {
|
||||
this.module = module;
|
||||
this.classSource = classSource;
|
||||
this.originalClassSource = originalClassSource;
|
||||
this.hierarchy = hierarchy;
|
||||
this.functionTypes = functionTypes;
|
||||
this.tagRegistry = tagRegistry;
|
||||
|
@ -195,6 +197,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
|
||||
private WasmGCCustomTypeMapperFactoryContext customTypeMapperFactoryContext() {
|
||||
return new WasmGCCustomTypeMapperFactoryContext() {
|
||||
@Override
|
||||
public ClassReaderSource originalClasses() {
|
||||
return originalClassSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassReaderSource classes() {
|
||||
return classSource;
|
||||
|
|
|
@ -22,6 +22,8 @@ import org.teavm.model.ClassReaderSource;
|
|||
public interface WasmGCCustomTypeMapperFactoryContext {
|
||||
ClassReaderSource classes();
|
||||
|
||||
ClassReaderSource originalClasses();
|
||||
|
||||
WasmModule module();
|
||||
|
||||
WasmGCClassInfoProvider classInfoProvider();
|
||||
|
|
|
@ -433,5 +433,10 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
public WasmGCStringProvider strings() {
|
||||
return context.strings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.generators.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.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.diagnostics.Diagnostics;
|
||||
|
@ -48,4 +50,6 @@ public interface WasmGCCustomGeneratorContext {
|
|||
Diagnostics diagnostics();
|
||||
|
||||
WasmGCStringProvider strings();
|
||||
|
||||
void addToInitializer(Consumer<WasmFunction> initializerContributor);
|
||||
}
|
||||
|
|
|
@ -108,6 +108,9 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
WasmGCCustomGenerator generator = null;
|
||||
for (var factory : factories) {
|
||||
generator = factory.createGenerator(method, factoryContext);
|
||||
if (generator != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = new Container(generator);
|
||||
generators.put(method, result);
|
||||
|
|
|
@ -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.backend.wasm.model.expression;
|
||||
|
||||
public class WasmExternConversion {
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.model.expression;
|
||||
|
||||
public enum WasmExternConversionType {
|
||||
EXTERN_TO_OBJECT,
|
||||
OBJECT_TO_EXTERN
|
||||
}
|
|
@ -42,7 +42,6 @@ import org.teavm.common.ServiceRepository;
|
|||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
|
@ -52,8 +51,6 @@ import org.teavm.model.ClassReaderSource;
|
|||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
@ -61,12 +58,7 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ReferenceCache;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.NullConstantInstruction;
|
||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||
import org.teavm.model.util.BasicBlockSplitter;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.parsing.Parser;
|
||||
|
@ -102,7 +94,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
private Diagnostics diagnostics;
|
||||
DefaultCallGraph callGraph = new DefaultCallGraph();
|
||||
private DependencyAgent agent;
|
||||
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
||||
Map<MethodReference, DependencyPlugin> dependencyPlugins = new HashMap<>();
|
||||
private boolean completing;
|
||||
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
|
||||
|
@ -119,7 +110,8 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
this.unprocessedClassSource = classSource;
|
||||
this.diagnostics = diagnostics;
|
||||
this.referenceCache = referenceCache;
|
||||
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache, platformTags);
|
||||
agent = new DependencyAgent(this);
|
||||
this.classSource = new DependencyClassSource(agent, classSource, diagnostics, incrementalCache, platformTags);
|
||||
agentClassSource = this.classSource;
|
||||
classHierarchy = new ClassHierarchy(this.classSource);
|
||||
this.classLoader = classLoader;
|
||||
|
@ -138,7 +130,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
|
||||
classCache = new CachedFunction<>(this::createClassDependency);
|
||||
|
||||
agent = new DependencyAgent(this);
|
||||
classType = getType("java.lang.Class");
|
||||
}
|
||||
|
||||
|
@ -274,7 +265,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
dep.used = false;
|
||||
lock(dep, false);
|
||||
deferredTasks.add(() -> {
|
||||
processInvokeDynamic(dep);
|
||||
classSource.getReferenceResolver().use(dep.method.getReference(), diagnostics);
|
||||
processMethod(dep);
|
||||
dep.used = true;
|
||||
|
@ -489,7 +479,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
void scheduleMethodAnalysis(MethodDependency dep) {
|
||||
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
|
||||
deferredTasks.add(() -> {
|
||||
processInvokeDynamic(dep);
|
||||
classSource.getReferenceResolver().use(dep.getReference(), diagnostics);
|
||||
processMethod(dep);
|
||||
});
|
||||
|
@ -755,6 +744,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
agent.cleanup();
|
||||
listeners.clear();
|
||||
|
||||
classSource.dispose();
|
||||
agentClassSource = classSourcePacker.pack(classSource,
|
||||
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
|
||||
if (classSource != agentClassSource) {
|
||||
|
@ -762,7 +752,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
||||
}
|
||||
classSource.innerHierarchy = null;
|
||||
classSource.dispose();
|
||||
|
||||
classSource = null;
|
||||
methodReaderCache = null;
|
||||
}
|
||||
|
@ -827,7 +817,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
}
|
||||
|
||||
public void addBootstrapMethodSubstitutor(MethodReference method, BootstrapMethodSubstitutor substitutor) {
|
||||
bootstrapMethodSubstitutors.put(method, substitutor);
|
||||
classSource.bootstrapMethodSubstitutors.put(method, substitutor);
|
||||
}
|
||||
|
||||
public void addDependencyPlugin(MethodReference method, DependencyPlugin dependencyPlugin) {
|
||||
|
@ -863,70 +853,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
return result;
|
||||
}
|
||||
|
||||
private void processInvokeDynamic(MethodDependency methodDep) {
|
||||
if (methodDep.method == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = methodDep.method.getProgram();
|
||||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramEmitter pe = ProgramEmitter.create(program, classHierarchy);
|
||||
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (Instruction insn : block) {
|
||||
if (!(insn instanceof InvokeDynamicInstruction)) {
|
||||
continue;
|
||||
}
|
||||
block = insn.getBasicBlock();
|
||||
|
||||
InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn;
|
||||
MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(),
|
||||
indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature());
|
||||
BootstrapMethodSubstitutor substitutor = bootstrapMethodSubstitutors.get(bootstrapMethod);
|
||||
if (substitutor == null) {
|
||||
NullConstantInstruction nullInsn = new NullConstantInstruction();
|
||||
nullInsn.setReceiver(indy.getReceiver());
|
||||
nullInsn.setLocation(indy.getLocation());
|
||||
insn.replace(nullInsn);
|
||||
CallLocation location = new CallLocation(methodDep.getReference(), insn.getLocation());
|
||||
diagnostics.error(location, "Substitutor for bootstrap method {{m0}} was not found",
|
||||
bootstrapMethod);
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicBlock splitBlock = splitter.split(block, insn);
|
||||
|
||||
pe.enter(block);
|
||||
pe.setCurrentLocation(indy.getLocation());
|
||||
insn.delete();
|
||||
|
||||
List<ValueEmitter> arguments = new ArrayList<>();
|
||||
for (int k = 0; k < indy.getArguments().size(); ++k) {
|
||||
arguments.add(pe.var(indy.getArguments().get(k), indy.getMethod().parameterType(k)));
|
||||
}
|
||||
DynamicCallSite callSite = new DynamicCallSite(
|
||||
methodDep.getReference(), indy.getMethod(),
|
||||
indy.getInstance() != null ? pe.var(indy.getInstance(),
|
||||
ValueType.object(methodDep.getMethod().getOwnerName())) : null,
|
||||
arguments, indy.getBootstrapMethod(), indy.getBootstrapArguments(),
|
||||
agent);
|
||||
ValueEmitter result = substitutor.substitute(callSite, pe);
|
||||
if (result.getVariable() != null && result.getVariable() != indy.getReceiver()
|
||||
&& indy.getReceiver() != null) {
|
||||
AssignInstruction assign = new AssignInstruction();
|
||||
assign.setAssignee(result.getVariable());
|
||||
assign.setReceiver(indy.getReceiver());
|
||||
pe.addInstruction(assign);
|
||||
}
|
||||
pe.jump(splitBlock);
|
||||
}
|
||||
}
|
||||
splitter.fixProgram();
|
||||
}
|
||||
|
||||
static class IncrementalCache implements IncrementalDependencyProvider, IncrementalDependencyRegistration {
|
||||
private final String[] emptyArray = new String[0];
|
||||
|
|
|
@ -17,12 +17,15 @@ package org.teavm.dependency;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
|
@ -30,12 +33,23 @@ import org.teavm.model.ClassHolderTransformer;
|
|||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.NullConstantInstruction;
|
||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||
import org.teavm.model.transformation.ClassInitInsertion;
|
||||
import org.teavm.model.util.BasicBlockSplitter;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
|
||||
class DependencyClassSource implements ClassHolderSource {
|
||||
private DependencyAgent agent;
|
||||
private ClassReaderSource innerSource;
|
||||
ClassHierarchy innerHierarchy;
|
||||
private Diagnostics diagnostics;
|
||||
|
@ -48,9 +62,12 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
private ReferenceResolver referenceResolver;
|
||||
private ClassInitInsertion classInitInsertion;
|
||||
private String entryPoint;
|
||||
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
||||
private boolean disposed;
|
||||
|
||||
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
|
||||
DependencyClassSource(DependencyAgent agent, ClassReaderSource innerSource, Diagnostics diagnostics,
|
||||
IncrementalDependencyRegistration dependencyRegistration, String[] platformTags) {
|
||||
this.agent = agent;
|
||||
this.innerSource = innerSource;
|
||||
this.diagnostics = diagnostics;
|
||||
innerHierarchy = new ClassHierarchy(innerSource);
|
||||
|
@ -65,7 +82,16 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
|
||||
@Override
|
||||
public ClassHolder get(String name) {
|
||||
return cache.computeIfAbsent(name, n -> Optional.ofNullable(findAndTransformClass(n))).orElse(null);
|
||||
var result = cache.get(name);
|
||||
if (result == null) {
|
||||
var cls = findClass(name);
|
||||
result = Optional.ofNullable(cls);
|
||||
cache.put(name, result);
|
||||
if (cls != null) {
|
||||
transformClass(cls);
|
||||
}
|
||||
}
|
||||
return result.orElse(null);
|
||||
}
|
||||
|
||||
public void submit(ClassHolder cls) {
|
||||
|
@ -84,26 +110,27 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
cache.remove(cls.getName());
|
||||
}
|
||||
|
||||
private ClassHolder findAndTransformClass(String name) {
|
||||
var cls = findClass(name);
|
||||
if (cls != null) {
|
||||
if (!transformers.isEmpty()) {
|
||||
for (var transformer : transformers) {
|
||||
transformer.transformClass(cls, transformContext);
|
||||
}
|
||||
}
|
||||
private void transformClass(ClassHolder cls) {
|
||||
if (!disposed) {
|
||||
for (var method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
var program = method.getProgram();
|
||||
method.setProgramSupplier(m -> {
|
||||
referenceResolver.resolve(m, program);
|
||||
classInitInsertion.apply(m, program);
|
||||
return program;
|
||||
});
|
||||
}
|
||||
processInvokeDynamic(method);
|
||||
}
|
||||
}
|
||||
if (!transformers.isEmpty()) {
|
||||
for (var transformer : transformers) {
|
||||
transformer.transformClass(cls, transformContext);
|
||||
}
|
||||
}
|
||||
for (var method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
var program = method.getProgram();
|
||||
method.setProgramSupplier(m -> {
|
||||
referenceResolver.resolve(m, program);
|
||||
classInitInsertion.apply(m, program);
|
||||
return program;
|
||||
});
|
||||
}
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
private ClassHolder findClass(String name) {
|
||||
|
@ -132,6 +159,69 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
|
||||
public void dispose() {
|
||||
transformers.clear();
|
||||
bootstrapMethodSubstitutors.clear();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
private void processInvokeDynamic(MethodHolder method) {
|
||||
Program program = method.getProgram();
|
||||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramEmitter pe = ProgramEmitter.create(program, innerHierarchy);
|
||||
BasicBlockSplitter splitter = new BasicBlockSplitter(program);
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (Instruction insn : block) {
|
||||
if (!(insn instanceof InvokeDynamicInstruction)) {
|
||||
continue;
|
||||
}
|
||||
block = insn.getBasicBlock();
|
||||
|
||||
InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn;
|
||||
MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(),
|
||||
indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature());
|
||||
BootstrapMethodSubstitutor substitutor = bootstrapMethodSubstitutors.get(bootstrapMethod);
|
||||
if (substitutor == null) {
|
||||
NullConstantInstruction nullInsn = new NullConstantInstruction();
|
||||
nullInsn.setReceiver(indy.getReceiver());
|
||||
nullInsn.setLocation(indy.getLocation());
|
||||
insn.replace(nullInsn);
|
||||
CallLocation location = new CallLocation(method.getReference(), insn.getLocation());
|
||||
diagnostics.error(location, "Substitutor for bootstrap method {{m0}} was not found",
|
||||
bootstrapMethod);
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicBlock splitBlock = splitter.split(block, insn);
|
||||
|
||||
pe.enter(block);
|
||||
pe.setCurrentLocation(indy.getLocation());
|
||||
insn.delete();
|
||||
|
||||
List<ValueEmitter> arguments = new ArrayList<>();
|
||||
for (int k = 0; k < indy.getArguments().size(); ++k) {
|
||||
arguments.add(pe.var(indy.getArguments().get(k), indy.getMethod().parameterType(k)));
|
||||
}
|
||||
DynamicCallSite callSite = new DynamicCallSite(
|
||||
method.getReference(), indy.getMethod(),
|
||||
indy.getInstance() != null ? pe.var(indy.getInstance(),
|
||||
ValueType.object(method.getOwnerName())) : null,
|
||||
arguments, indy.getBootstrapMethod(), indy.getBootstrapArguments(),
|
||||
agent);
|
||||
ValueEmitter result = substitutor.substitute(callSite, pe);
|
||||
if (result.getVariable() != null && result.getVariable() != indy.getReceiver()
|
||||
&& indy.getReceiver() != null) {
|
||||
AssignInstruction assign = new AssignInstruction();
|
||||
assign.setAssignee(result.getVariable());
|
||||
assign.setReceiver(indy.getReceiver());
|
||||
pe.addInstruction(assign);
|
||||
}
|
||||
pe.jump(splitBlock);
|
||||
}
|
||||
}
|
||||
splitter.fixProgram();
|
||||
}
|
||||
|
||||
final ClassHolderTransformerContext transformContext = new ClassHolderTransformerContext() {
|
||||
|
|
|
@ -118,4 +118,8 @@ let $rt_apply = (instance, method, args) => instance[method].apply(instance, arg
|
|||
|
||||
let $rt_apply_topLevel = (method, args) => method.apply(null, args);
|
||||
|
||||
let $rt_skip = (array, count) => count === 0 ? array : Array.prototype.slice.call(array, count);
|
||||
let $rt_skip = (array, count) => count === 0 ? array : Array.prototype.slice.call(array, count);
|
||||
|
||||
let $rt_callWithReceiver = f => function() {
|
||||
return f.apply(null, [this].concat(Array.prototype.slice.call(arguments)));
|
||||
}
|
|
@ -20,6 +20,9 @@ TeaVM.wasm = function() {
|
|||
let getGlobalName = function(name) {
|
||||
return eval(name);
|
||||
}
|
||||
let javaObjectSymbol = Symbol("javaObject");
|
||||
let functionsSymbol = Symbol("functions");
|
||||
let javaWrappers = new WeakMap();
|
||||
function defaults(imports) {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
|
@ -32,42 +35,28 @@ TeaVM.wasm = function() {
|
|||
exports.reportGarbageCollectedString(heldValue);
|
||||
});
|
||||
imports.teavmDate = {
|
||||
currentTimeMillis() {
|
||||
return new Date().getTime();
|
||||
},
|
||||
dateToString(timestamp) {
|
||||
return stringToJava(new Date(timestamp).toString());
|
||||
},
|
||||
getYear(timestamp) {
|
||||
return new Date(timestamp).getFullYear();
|
||||
},
|
||||
currentTimeMillis: () => new Date().getTime(),
|
||||
dateToString: timestamp => stringToJava(new Date(timestamp).toString()),
|
||||
getYear: timestamp =>new Date(timestamp).getFullYear(),
|
||||
setYear(timestamp, year) {
|
||||
let date = new Date(timestamp);
|
||||
date.setFullYear(year);
|
||||
return date.getTime();
|
||||
},
|
||||
getMonth(timestamp) {
|
||||
return new Date(timestamp).getMonth();
|
||||
},
|
||||
getMonth: timestamp =>new Date(timestamp).getMonth(),
|
||||
setMonth(timestamp, month) {
|
||||
let date = new Date(timestamp);
|
||||
date.setMonth(month);
|
||||
return date.getTime();
|
||||
},
|
||||
getDate(timestamp) {
|
||||
return new Date(timestamp).getDate();
|
||||
},
|
||||
getDate: timestamp =>new Date(timestamp).getDate(),
|
||||
setDate(timestamp, value) {
|
||||
let date = new Date(timestamp);
|
||||
date.setDate(value);
|
||||
return date.getTime();
|
||||
},
|
||||
create(year, month, date, hrs, min, sec) {
|
||||
return new Date(year, month, date, hrs, min, sec).getTime();
|
||||
},
|
||||
createFromUTC(year, month, date, hrs, min, sec) {
|
||||
return Date.UTC(year, month, date, hrs, min, sec);
|
||||
}
|
||||
create: (year, month, date, hrs, min, sec) => new Date(year, month, date, hrs, min, sec).getTime(),
|
||||
createFromUTC: (year, month, date, hrs, min, sec) => Date.UTC(year, month, date, hrs, min, sec)
|
||||
};
|
||||
imports.teavmConsole = {
|
||||
putcharStderr(c) {
|
||||
|
@ -106,6 +95,22 @@ TeaVM.wasm = function() {
|
|||
function identity(value) {
|
||||
return value;
|
||||
}
|
||||
function sanitizeName(str) {
|
||||
let result = "";
|
||||
let firstChar = str.charAt(0);
|
||||
result += isIdentifierStart(firstChar) ? firstChar : '_';
|
||||
for (let i = 1; i < str.length; ++i) {
|
||||
let c = str.charAt(i)
|
||||
result += isIdentifierPart(c) ? c : '_';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function isIdentifierStart(s) {
|
||||
return s >= 'A' && s <= 'Z' || s >= 'a' && s <= 'z' || s === '_' || s === '$';
|
||||
}
|
||||
function isIdentifierPart(s) {
|
||||
return isIdentifierStart(s) || s >= '0' && s <= '9';
|
||||
}
|
||||
imports.teavmJso = {
|
||||
emptyString: () => "",
|
||||
stringFromCharCode: code => String.fromCharCode(code),
|
||||
|
@ -120,25 +125,76 @@ TeaVM.wasm = function() {
|
|||
getPropertyPure: (obj, prop) => obj[prop],
|
||||
setProperty: (obj, prop, value) => obj[prop] = value,
|
||||
setPropertyPure: (obj, prop) => obj[prop] = value,
|
||||
global: getGlobalName
|
||||
global: getGlobalName,
|
||||
createClass(name) {
|
||||
let fn = new Function(
|
||||
"javaObjectSymbol",
|
||||
"functionsSymbol",
|
||||
`return function JavaClass_${sanitizeName(name)}(javaObject) {
|
||||
this[javaObjectSymbol] = javaObject;
|
||||
this[functionsSymbol] = null;
|
||||
};`
|
||||
);
|
||||
return fn(javaObjectSymbol, functionsSymbol);
|
||||
},
|
||||
defineMethod(cls, name, fn) {
|
||||
cls.prototype[name] = function(...args) {
|
||||
return fn(this, ...args);
|
||||
}
|
||||
},
|
||||
defineProperty(cls, name, getFn, setFn) {
|
||||
let descriptor = {
|
||||
get() {
|
||||
return getFn(this);
|
||||
}
|
||||
};
|
||||
if (setFn !== null) {
|
||||
descriptor.set = function(value) {
|
||||
setFn(this, value);
|
||||
}
|
||||
}
|
||||
Object.defineProperty(cls.prototype, name, descriptor);
|
||||
},
|
||||
javaObjectToJS(instance, cls) {
|
||||
let existing = javaWrappers.get(instance);
|
||||
if (typeof existing != "undefined") {
|
||||
let result = existing.deref();
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
let obj = new cls(instance);
|
||||
javaWrappers.set(instance, new WeakRef(obj));
|
||||
return obj;
|
||||
},
|
||||
unwrapJavaObject(instance) {
|
||||
return instance[javaObjectSymbol];
|
||||
},
|
||||
asFunction(instance, propertyName) {
|
||||
let functions = instance[functionsSymbol];
|
||||
if (functions === null) {
|
||||
functions = Object.create(null);
|
||||
instance[functionsSymbol] = functions;
|
||||
}
|
||||
let result = functions[propertyName];
|
||||
if (typeof result !== 'function') {
|
||||
result = function() {
|
||||
return instance[propertyName].apply(instance, arguments);
|
||||
}
|
||||
functions[propertyName] = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
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.teavmJso["createFunction" + i] = (...args) => new Function(...args);
|
||||
imports.teavmJso["callFunction" + i] = (fn, ...args) => fn(...args);
|
||||
imports.teavmJso["callMethod" + i] = (instance, method, ...args) => instance[method](...args);
|
||||
imports.teavmJso["construct" + i] = (constructor, ...args) => new constructor(...args);
|
||||
}
|
||||
imports.teavmMath = Math;
|
||||
}
|
||||
|
|
129
jso/impl/src/main/java/org/teavm/jso/impl/AliasCollector.java
Normal file
129
jso/impl/src/main/java/org/teavm/jso/impl/AliasCollector.java
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class AliasCollector {
|
||||
private AliasCollector() {
|
||||
}
|
||||
|
||||
public static boolean isStaticMember(MethodReader method) {
|
||||
return !isInstanceMember(method);
|
||||
}
|
||||
|
||||
public static boolean isInstanceMember(MethodReader method) {
|
||||
return method.getAnnotations().get(JSInstanceExpose.class.getName()) != null;
|
||||
}
|
||||
|
||||
public static Members collectMembers(ClassReader classReader, Predicate<MethodReader> filter) {
|
||||
var methods = new HashMap<String, MethodReference>();
|
||||
var properties = new HashMap<String, PropertyInfo>();
|
||||
MethodReference constructor = null;
|
||||
for (var method : classReader.getMethods()) {
|
||||
if (!filter.test(method)) {
|
||||
continue;
|
||||
}
|
||||
var methodAlias = getPublicAlias(method);
|
||||
if (methodAlias != null) {
|
||||
switch (methodAlias.kind) {
|
||||
case METHOD:
|
||||
methods.put(methodAlias.name, method.getReference());
|
||||
break;
|
||||
case GETTER: {
|
||||
var propInfo = properties.computeIfAbsent(methodAlias.name, k -> new PropertyInfo());
|
||||
propInfo.getter = method.getReference();
|
||||
break;
|
||||
}
|
||||
case SETTER: {
|
||||
var propInfo = properties.computeIfAbsent(methodAlias.name, k -> new PropertyInfo());
|
||||
propInfo.setter = method.getReference();
|
||||
break;
|
||||
}
|
||||
case CONSTRUCTOR:
|
||||
constructor = method.getReference();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Members(methods, properties, constructor);
|
||||
}
|
||||
|
||||
public static Alias getPublicAlias(MethodReader method) {
|
||||
var annot = method.getAnnotations().get(JSMethodToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(annot.getValue("name").getString(), AliasKind.METHOD);
|
||||
}
|
||||
|
||||
annot = method.getAnnotations().get(JSGetterToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(annot.getValue("name").getString(), AliasKind.GETTER);
|
||||
}
|
||||
|
||||
annot = method.getAnnotations().get(JSSetterToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(annot.getValue("name").getString(), AliasKind.SETTER);
|
||||
}
|
||||
|
||||
annot = method.getAnnotations().get(JSConstructorToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(null, AliasKind.CONSTRUCTOR);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Members {
|
||||
public final Map<String, MethodReference> methods;
|
||||
public final Map<String, PropertyInfo> properties;
|
||||
public final MethodReference constructor;
|
||||
|
||||
Members(Map<String, MethodReference> methods, Map<String, PropertyInfo> properties,
|
||||
MethodReference constructor) {
|
||||
this.methods = methods;
|
||||
this.properties = properties;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class PropertyInfo {
|
||||
public MethodReference getter;
|
||||
public MethodReference setter;
|
||||
}
|
||||
|
||||
public static class Alias {
|
||||
public final String name;
|
||||
public final AliasKind kind;
|
||||
|
||||
Alias(String name, AliasKind kind) {
|
||||
this.name = name;
|
||||
this.kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
public enum AliasKind {
|
||||
METHOD,
|
||||
GETTER,
|
||||
SETTER,
|
||||
CONSTRUCTOR
|
||||
}
|
||||
}
|
|
@ -738,6 +738,7 @@ public final class JS {
|
|||
|
||||
@GeneratedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@Import(name = "asFunction", module = "teavmJso")
|
||||
public static native JSObject function(JSObject instance, JSObject property);
|
||||
|
||||
@GeneratedBy(JSNativeGenerator.class)
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package org.teavm.jso.impl;
|
||||
|
||||
import static org.teavm.jso.impl.AliasCollector.collectMembers;
|
||||
import static org.teavm.jso.impl.AliasCollector.getPublicAlias;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.rendering.RenderingManager;
|
||||
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||
|
@ -47,7 +47,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
public void begin(RenderingManager context, BuildTarget buildTarget) {
|
||||
writer = context.getWriter();
|
||||
classSource = context.getClassSource();
|
||||
typeHelper = new JSTypeHelper(context.getClassSource());
|
||||
typeHelper = new JSTypeHelper(context.getOriginalClassSource());
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
}
|
||||
|
||||
private boolean exportClassInstanceMembers(ClassReader classReader) {
|
||||
var members = collectMembers(classReader, method -> !method.hasModifier(ElementModifier.STATIC));
|
||||
var members = collectMembers(classReader, AliasCollector::isInstanceMember);
|
||||
|
||||
var isJsClassImpl = typeHelper.isJavaScriptImplementation(classReader.getName());
|
||||
if (members.methods.isEmpty() && members.properties.isEmpty() && !isJsClassImpl) {
|
||||
|
@ -106,23 +106,25 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
}
|
||||
|
||||
for (var aliasEntry : members.methods.entrySet()) {
|
||||
if (classReader.getMethod(aliasEntry.getValue()) == null) {
|
||||
if (classReader.getMethod(aliasEntry.getValue().getDescriptor()) == null) {
|
||||
continue;
|
||||
}
|
||||
appendMethodAlias(aliasEntry.getKey());
|
||||
writer.ws().append("=").ws().append("c.").appendVirtualMethod(aliasEntry.getValue())
|
||||
.append(";").softNewLine();
|
||||
writer.ws().append("=").ws().appendFunction("$rt_callWithReceiver").append("(")
|
||||
.appendMethod(aliasEntry.getValue()).append(");").softNewLine();
|
||||
}
|
||||
for (var aliasEntry : members.properties.entrySet()) {
|
||||
var propInfo = aliasEntry.getValue();
|
||||
if (propInfo.getter == null || classReader.getMethod(propInfo.getter) == null) {
|
||||
if (propInfo.getter == null || classReader.getMethod(propInfo.getter.getDescriptor()) == null) {
|
||||
continue;
|
||||
}
|
||||
appendPropertyAlias(aliasEntry.getKey());
|
||||
writer.append("get:").ws().append("c.").appendVirtualMethod(propInfo.getter);
|
||||
if (propInfo.setter != null && classReader.getMethod(propInfo.setter) != null) {
|
||||
writer.append("get:").ws().appendFunction("$rt_callWithReceiver").append("(")
|
||||
.appendMethod(propInfo.getter).append(")");
|
||||
if (propInfo.setter != null && classReader.getMethod(propInfo.setter.getDescriptor()) != null) {
|
||||
writer.append(",").softNewLine();
|
||||
writer.append("set:").ws().append("c.").appendVirtualMethod(propInfo.setter);
|
||||
writer.append("set:").ws().appendFunction("$rt_callWithReceiver").append("(")
|
||||
.appendMethod(propInfo.setter).append(")");
|
||||
}
|
||||
writer.softNewLine().outdent().append("});").softNewLine();
|
||||
}
|
||||
|
@ -136,7 +138,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
}
|
||||
|
||||
private boolean exportClassStaticMembers(ClassReader classReader, String name) {
|
||||
var members = collectMembers(classReader, c -> c.hasModifier(ElementModifier.STATIC));
|
||||
var members = collectMembers(classReader, AliasCollector::isStaticMember);
|
||||
|
||||
if (members.methods.isEmpty() && members.properties.isEmpty()) {
|
||||
return false;
|
||||
|
@ -146,7 +148,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
|
||||
for (var aliasEntry : members.methods.entrySet()) {
|
||||
appendMethodAlias(aliasEntry.getKey());
|
||||
var fullRef = new MethodReference(classReader.getName(), aliasEntry.getValue());
|
||||
var fullRef = aliasEntry.getValue();
|
||||
writer.ws().append("=").ws().appendMethod(fullRef).append(";").softNewLine();
|
||||
}
|
||||
for (var aliasEntry : members.properties.entrySet()) {
|
||||
|
@ -155,11 +157,11 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
continue;
|
||||
}
|
||||
appendPropertyAlias(aliasEntry.getKey());
|
||||
var fullGetter = new MethodReference(classReader.getName(), propInfo.getter);
|
||||
var fullGetter = propInfo.getter;
|
||||
writer.append("get:").ws().appendMethod(fullGetter);
|
||||
if (propInfo.setter != null) {
|
||||
writer.append(",").softNewLine();
|
||||
var fullSetter = new MethodReference(classReader.getName(), propInfo.setter);
|
||||
var fullSetter = propInfo.setter;
|
||||
writer.append("set:").ws().appendMethod(fullSetter);
|
||||
}
|
||||
writer.softNewLine().outdent().append("});").softNewLine();
|
||||
|
@ -182,39 +184,6 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
.ws().append("{").indent().softNewLine();
|
||||
}
|
||||
|
||||
private Members collectMembers(ClassReader classReader, Predicate<MethodReader> filter) {
|
||||
var methods = new HashMap<String, MethodDescriptor>();
|
||||
var properties = new HashMap<String, PropertyInfo>();
|
||||
MethodDescriptor constructor = null;
|
||||
for (var method : classReader.getMethods()) {
|
||||
if (!filter.test(method)) {
|
||||
continue;
|
||||
}
|
||||
var methodAlias = getPublicAlias(method);
|
||||
if (methodAlias != null) {
|
||||
switch (methodAlias.kind) {
|
||||
case METHOD:
|
||||
methods.put(methodAlias.name, method.getDescriptor());
|
||||
break;
|
||||
case GETTER: {
|
||||
var propInfo = properties.computeIfAbsent(methodAlias.name, k -> new PropertyInfo());
|
||||
propInfo.getter = method.getDescriptor();
|
||||
break;
|
||||
}
|
||||
case SETTER: {
|
||||
var propInfo = properties.computeIfAbsent(methodAlias.name, k -> new PropertyInfo());
|
||||
propInfo.setter = method.getDescriptor();
|
||||
break;
|
||||
}
|
||||
case CONSTRUCTOR:
|
||||
constructor = method.getDescriptor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Members(methods, properties, constructor);
|
||||
}
|
||||
|
||||
private void exportModule() {
|
||||
var cls = classSource.get(context.getEntryPoint());
|
||||
for (var method : cls.getMethods()) {
|
||||
|
@ -222,7 +191,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
continue;
|
||||
}
|
||||
var methodAlias = getPublicAlias(method);
|
||||
if (methodAlias != null && methodAlias.kind == AliasKind.METHOD) {
|
||||
if (methodAlias != null && methodAlias.kind == AliasCollector.AliasKind.METHOD) {
|
||||
context.exportMethod(method.getReference(), methodAlias.name);
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +199,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
|
||||
private void exportClassFromModule(ClassReader cls, String functionName) {
|
||||
var name = getClassAliasName(cls);
|
||||
var constructors = collectMembers(cls, method -> !method.hasModifier(ElementModifier.STATIC));
|
||||
var constructors = collectMembers(cls, AliasCollector::isInstanceMember);
|
||||
|
||||
var method = constructors.constructor;
|
||||
writer.append("function ").appendFunction(functionName).append("(");
|
||||
|
@ -245,7 +214,7 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
writer.append(")").ws().appendBlockStart();
|
||||
if (method != null) {
|
||||
writer.appendClass(cls.getName()).append(".call(this);").softNewLine();
|
||||
writer.appendMethod(new MethodReference(cls.getName(), method)).append("(this");
|
||||
writer.appendMethod(method).append("(this");
|
||||
for (var i = 0; i < method.parameterCount(); ++i) {
|
||||
writer.append(",").ws().append("p" + i);
|
||||
}
|
||||
|
@ -293,31 +262,6 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Alias getPublicAlias(MethodReader method) {
|
||||
var annot = method.getAnnotations().get(JSMethodToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(annot.getValue("name").getString(), AliasKind.METHOD);
|
||||
}
|
||||
|
||||
annot = method.getAnnotations().get(JSGetterToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(annot.getValue("name").getString(), AliasKind.GETTER);
|
||||
}
|
||||
|
||||
annot = method.getAnnotations().get(JSSetterToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(annot.getValue("name").getString(), AliasKind.SETTER);
|
||||
}
|
||||
|
||||
annot = method.getAnnotations().get(JSConstructorToExpose.class.getName());
|
||||
if (annot != null) {
|
||||
return new Alias(null, AliasKind.CONSTRUCTOR);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private FieldReader getFunctorField(ClassReader cls) {
|
||||
return cls.getField("$$jso_functor$$");
|
||||
}
|
||||
|
@ -403,38 +347,5 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
|||
return methodReader != null && getPublicAlias(methodReader) != null;
|
||||
}
|
||||
|
||||
private static class Members {
|
||||
final Map<String, MethodDescriptor> methods;
|
||||
final Map<String, PropertyInfo> properties;
|
||||
final MethodDescriptor constructor;
|
||||
|
||||
Members(Map<String, MethodDescriptor> methods, Map<String, PropertyInfo> properties,
|
||||
MethodDescriptor constructor) {
|
||||
this.methods = methods;
|
||||
this.properties = properties;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PropertyInfo {
|
||||
MethodDescriptor getter;
|
||||
MethodDescriptor setter;
|
||||
}
|
||||
|
||||
private static class Alias {
|
||||
final String name;
|
||||
final AliasKind kind;
|
||||
|
||||
Alias(String name, AliasKind kind) {
|
||||
this.name = name;
|
||||
this.kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
private enum AliasKind {
|
||||
METHOD,
|
||||
GETTER,
|
||||
SETTER,
|
||||
CONSTRUCTOR
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.jso.impl;
|
||||
|
||||
@interface JSBodyDelegate {
|
||||
}
|
|
@ -80,20 +80,6 @@ import org.teavm.model.util.ProgramUtils;
|
|||
|
||||
class JSClassProcessor {
|
||||
private static final String NO_SIDE_EFFECTS = NoSideEffects.class.getName();
|
||||
private static final MethodReference WRAP = new MethodReference(JSWrapper.class, "wrap", Object.class,
|
||||
Object.class);
|
||||
private static final MethodReference MAYBE_WRAP = new MethodReference(JSWrapper.class, "maybeWrap", Object.class,
|
||||
Object.class);
|
||||
private static final MethodReference UNWRAP = new MethodReference(JSWrapper.class, "unwrap", Object.class,
|
||||
JSObject.class);
|
||||
private static final MethodReference MAYBE_UNWRAP = new MethodReference(JSWrapper.class, "maybeUnwrap",
|
||||
Object.class, JSObject.class);
|
||||
private static final MethodReference IS_JS = new MethodReference(JSWrapper.class, "isJs",
|
||||
Object.class, boolean.class);
|
||||
private static final MethodReference IS_PRIMITIVE = new MethodReference(JSWrapper.class, "isPrimitive",
|
||||
Object.class, JSObject.class, boolean.class);
|
||||
private static final MethodReference INSTANCE_OF = new MethodReference(JSWrapper.class, "instanceOf",
|
||||
Object.class, JSObject.class, boolean.class);
|
||||
private final ClassReaderSource classSource;
|
||||
private final JSBodyRepository repository;
|
||||
private final JavaInvocationProcessor javaInvocationProcessor;
|
||||
|
@ -111,6 +97,7 @@ class JSClassProcessor {
|
|||
private JSImportAnnotationCache annotationCache;
|
||||
private ClassReader objectClass;
|
||||
private Predicate<String> classFilter = n -> true;
|
||||
private boolean wasmGC;
|
||||
|
||||
JSClassProcessor(ClassReaderSource classSource, JSTypeHelper typeHelper, JSBodyRepository repository,
|
||||
Diagnostics diagnostics, IncrementalDependencyRegistration incrementalCache, boolean strict) {
|
||||
|
@ -125,7 +112,11 @@ class JSClassProcessor {
|
|||
annotationCache = new JSImportAnnotationCache(classSource, diagnostics);
|
||||
}
|
||||
|
||||
public void setClassFilter(Predicate<String> classFilter) {
|
||||
void setWasmGC(boolean wasmGC) {
|
||||
this.wasmGC = wasmGC;
|
||||
}
|
||||
|
||||
void setClassFilter(Predicate<String> classFilter) {
|
||||
this.classFilter = classFilter;
|
||||
}
|
||||
|
||||
|
@ -286,8 +277,11 @@ class JSClassProcessor {
|
|||
|
||||
void processProgram(MethodHolder methodToProcess) {
|
||||
setCurrentProgram(methodToProcess.getProgram());
|
||||
types = new JSTypeInference(typeHelper, classSource, program, methodToProcess.getReference());
|
||||
types = new JSTypeInference(typeHelper, classSource, program, methodToProcess.getReference(), wasmGC);
|
||||
types.ensure();
|
||||
if (wasmGC) {
|
||||
wrapJsPhis(methodToProcess.getReference());
|
||||
}
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
var block = program.basicBlockAt(i);
|
||||
for (var insn : block) {
|
||||
|
@ -355,6 +349,32 @@ class JSClassProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private void wrapJsPhis(MethodReference methodReference) {
|
||||
var changed = false;
|
||||
for (var block : program.getBasicBlocks()) {
|
||||
for (var phi : block.getPhis()) {
|
||||
if (types.typeOf(phi.getReceiver()) == JSType.MIXED) {
|
||||
for (var incoming : phi.getIncomings()) {
|
||||
if (types.typeOf(incoming.getValue()) == JSType.JS) {
|
||||
changed = true;
|
||||
var wrap = new InvokeInstruction();
|
||||
wrap.setType(InvocationType.SPECIAL);
|
||||
wrap.setMethod(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class));
|
||||
wrap.setArguments(incoming.getValue());
|
||||
wrap.setReceiver(program.createVariable());
|
||||
incoming.getSource().getLastInstruction().insertPrevious(wrap);
|
||||
incoming.setValue(wrap.getReceiver());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
types = new JSTypeInference(typeHelper, classSource, program, methodReference, wasmGC);
|
||||
types.ensure();
|
||||
}
|
||||
}
|
||||
|
||||
private void processInvokeArgs(InvokeInstruction invoke, MethodReader methodToInvoke) {
|
||||
if (methodToInvoke != null && methodToInvoke.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||
return;
|
||||
|
@ -402,7 +422,7 @@ class JSClassProcessor {
|
|||
if (type == JSType.JS || type == JSType.MIXED) {
|
||||
var unwrap = new InvokeInstruction();
|
||||
unwrap.setType(InvocationType.SPECIAL);
|
||||
unwrap.setMethod(type == JSType.MIXED ? MAYBE_UNWRAP : UNWRAP);
|
||||
unwrap.setMethod(type == JSType.MIXED ? JSMethods.MAYBE_UNWRAP : JSMethods.UNWRAP);
|
||||
unwrap.setArguments(program.createVariable());
|
||||
unwrap.setReceiver(insn.getReceiver());
|
||||
unwrap.setLocation(insn.getLocation());
|
||||
|
@ -420,7 +440,7 @@ class JSClassProcessor {
|
|||
if (type == JSType.JS || type == JSType.MIXED) {
|
||||
var wrap = new InvokeInstruction();
|
||||
wrap.setType(InvocationType.SPECIAL);
|
||||
wrap.setMethod(type == JSType.MIXED ? MAYBE_WRAP : WRAP);
|
||||
wrap.setMethod(type == JSType.MIXED ? JSMethods.MAYBE_WRAP : JSMethods.WRAP);
|
||||
wrap.setArguments(insn.getValue());
|
||||
wrap.setReceiver(program.createVariable());
|
||||
wrap.setLocation(insn.getLocation());
|
||||
|
@ -563,7 +583,7 @@ class JSClassProcessor {
|
|||
if (isTransparent(targetClassName)) {
|
||||
var invoke = new InvokeInstruction();
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
invoke.setMethod(IS_JS);
|
||||
invoke.setMethod(JSMethods.IS_JS);
|
||||
invoke.setArguments(value);
|
||||
invoke.setReceiver(receiver);
|
||||
invoke.setLocation(location);
|
||||
|
@ -572,7 +592,9 @@ class JSClassProcessor {
|
|||
var primitiveType = getPrimitiveType(targetClassName);
|
||||
var invoke = new InvokeInstruction();
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
invoke.setMethod(primitiveType != null ? IS_PRIMITIVE : INSTANCE_OF);
|
||||
invoke.setMethod(primitiveType != null
|
||||
? JSMethods.WRAPPER_IS_PRIMITIVE
|
||||
: JSMethods.WRAPPER_INSTANCE_OF);
|
||||
var secondArg = primitiveType != null
|
||||
? marshaller.addJsString(primitiveType, location)
|
||||
: marshaller.classRef(targetClassName, location);
|
||||
|
@ -648,7 +670,7 @@ class JSClassProcessor {
|
|||
}
|
||||
var wrap = new InvokeInstruction();
|
||||
wrap.setType(InvocationType.SPECIAL);
|
||||
wrap.setMethod(varType == JSType.JS ? WRAP : MAYBE_WRAP);
|
||||
wrap.setMethod(varType == JSType.JS ? JSMethods.WRAP : JSMethods.MAYBE_WRAP);
|
||||
wrap.setArguments(var);
|
||||
wrap.setReceiver(program.createVariable());
|
||||
wrap.setLocation(instruction.getLocation());
|
||||
|
@ -663,7 +685,7 @@ class JSClassProcessor {
|
|||
}
|
||||
var unwrap = new InvokeInstruction();
|
||||
unwrap.setType(InvocationType.SPECIAL);
|
||||
unwrap.setMethod(varType == JSType.JAVA ? UNWRAP : MAYBE_UNWRAP);
|
||||
unwrap.setMethod(varType == JSType.JAVA ? JSMethods.UNWRAP : JSMethods.MAYBE_UNWRAP);
|
||||
unwrap.setArguments(var);
|
||||
unwrap.setReceiver(program.createVariable());
|
||||
unwrap.setLocation(instruction.getLocation());
|
||||
|
@ -1187,6 +1209,7 @@ class JSClassProcessor {
|
|||
if (method.getAnnotations().get(NoSideEffects.class.getName()) != null) {
|
||||
proxyMethod.getAnnotations().add(new AnnotationHolder(NoSideEffects.class.getName()));
|
||||
}
|
||||
proxyMethod.getAnnotations().add(new AnnotationHolder(JSBodyDelegate.class.getName()));
|
||||
boolean inline = repository.inlineMethods.contains(methodRef);
|
||||
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
||||
? DynamicInjector.class.getName() : DynamicGenerator.class.getName());
|
||||
|
|
|
@ -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;
|
||||
|
||||
@interface JSClassToExpose {
|
||||
}
|
|
@ -17,23 +17,29 @@ package org.teavm.jso.impl;
|
|||
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.jso.JSExportClasses;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class JSDependencyListener extends AbstractDependencyListener {
|
||||
private JSBodyRepository repository;
|
||||
private DependencyNode exceptions;
|
||||
|
||||
JSDependencyListener(JSBodyRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
exceptions = agent.createNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
MethodReference ref = method.getReference();
|
||||
|
@ -43,6 +49,22 @@ class JSDependencyListener extends AbstractDependencyListener {
|
|||
agent.linkMethod(callbackMethod).addLocation(new CallLocation(ref)).use();
|
||||
}
|
||||
}
|
||||
if (method.getMethod().getAnnotations().get(JSBodyDelegate.class.getName()) != null) {
|
||||
exceptions.connect(method.getThrown());
|
||||
}
|
||||
if (method.getMethod().getOwnerName().equals(JS.class.getName())) {
|
||||
switch (method.getMethod().getName()) {
|
||||
case "invoke":
|
||||
case "construct":
|
||||
case "apply":
|
||||
case "get":
|
||||
case "getPure":
|
||||
case "set":
|
||||
case "setPure":
|
||||
exceptions.connect(method.getThrown());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,10 +84,8 @@ class JSDependencyListener extends AbstractDependencyListener {
|
|||
if (exposeAnnot != null) {
|
||||
MethodDependency methodDep = agent.linkMethod(method.getReference());
|
||||
if (methodDep.getMethod() != null) {
|
||||
if (!methodDep.getMethod().hasModifier(ElementModifier.STATIC)) {
|
||||
methodDep.getVariable(0).propagate(agent.getType(className));
|
||||
}
|
||||
methodDep.use();
|
||||
methodDep.getThrown().connect(exceptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
@interface JSInstanceExpose {
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.teavm.jso.JSObject;
|
||||
|
||||
public interface JSMarshallable {
|
||||
JSObject marshallToJs();
|
||||
}
|
|
@ -19,10 +19,11 @@ import java.util.Arrays;
|
|||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.core.JSArray;
|
||||
import org.teavm.jso.core.JSArrayReader;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
final class JSMethods {
|
||||
public final class JSMethods {
|
||||
public static final MethodReference GET = new MethodReference(JS.class, "get", JSObject.class,
|
||||
JSObject.class, JSObject.class);
|
||||
public static final MethodReference GET_PURE = new MethodReference(JS.class, "getPure", JSObject.class,
|
||||
|
@ -143,6 +144,25 @@ final class JSMethods {
|
|||
private static final MethodReference[] CONSTRUCT_METHODS = new MethodReference[13];
|
||||
private static final MethodReference[] ARRAY_OF_METHODS = new MethodReference[13];
|
||||
|
||||
public static final MethodReference WRAP = new MethodReference(JSWrapper.class, "wrap", JSObject.class,
|
||||
Object.class);
|
||||
public static final MethodReference MAYBE_WRAP = new MethodReference(JSWrapper.class, "maybeWrap", Object.class,
|
||||
Object.class);
|
||||
public static final MethodReference UNWRAP = new MethodReference(JSWrapper.class, "unwrap", Object.class,
|
||||
JSObject.class);
|
||||
public static final MethodReference MAYBE_UNWRAP = new MethodReference(JSWrapper.class, "maybeUnwrap",
|
||||
Object.class, JSObject.class);
|
||||
public static final MethodReference IS_JS = new MethodReference(JSWrapper.class, "isJs",
|
||||
Object.class, boolean.class);
|
||||
public static final MethodReference WRAPPER_IS_PRIMITIVE = new MethodReference(JSWrapper.class, "isPrimitive",
|
||||
Object.class, JSObject.class, boolean.class);
|
||||
public static final MethodReference WRAPPER_INSTANCE_OF = new MethodReference(JSWrapper.class, "instanceOf",
|
||||
Object.class, JSObject.class, boolean.class);
|
||||
|
||||
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) {
|
||||
var signature = new ValueType[i + 3];
|
||||
|
|
|
@ -46,12 +46,7 @@ public class JSOPlugin implements TeaVMPlugin {
|
|||
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);
|
||||
host.add(wrapperDependency);
|
||||
|
||||
TeaVMPluginUtil.handleNatives(host, JS.class);
|
||||
|
||||
|
@ -61,6 +56,7 @@ public class JSOPlugin implements TeaVMPlugin {
|
|||
|
||||
if (wasmGCHost != null) {
|
||||
classTransformer.setClassFilter(n -> !n.startsWith("java."));
|
||||
classTransformer.forWasmGC();
|
||||
WasmGCJso.install(host, wasmGCHost, repository);
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +86,10 @@ public class JSOPlugin implements TeaVMPlugin {
|
|||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "dependencyJsToJava", JSObject.class, Object.class),
|
||||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "marshallJavaToJs", Object.class, JSObject.class),
|
||||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "unmarshallJavaFromJs", JSObject.class, Object.class),
|
||||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "isJava", Object.class, boolean.class),
|
||||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "isJava", JSObject.class, boolean.class),
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.teavm.model.MethodReference;
|
|||
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.ExitInstruction;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
@ -61,6 +62,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
private ClassHierarchy hierarchy;
|
||||
private Map<String, ExposedClass> exposedClasses = new HashMap<>();
|
||||
private Predicate<String> classFilter = n -> true;
|
||||
private boolean wasmGC;
|
||||
|
||||
JSObjectClassTransformer(JSBodyRepository repository) {
|
||||
this.repository = repository;
|
||||
|
@ -70,6 +72,10 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
this.classFilter = classFilter;
|
||||
}
|
||||
|
||||
void forWasmGC() {
|
||||
wasmGC = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
this.hierarchy = context.getHierarchy();
|
||||
|
@ -77,6 +83,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
typeHelper = new JSTypeHelper(hierarchy.getClassSource());
|
||||
processor = new JSClassProcessor(hierarchy.getClassSource(), typeHelper, repository,
|
||||
context.getDiagnostics(), context.getIncrementalCache(), context.isStrict());
|
||||
processor.setWasmGC(wasmGC);
|
||||
processor.setClassFilter(classFilter);
|
||||
}
|
||||
processor.processClass(cls);
|
||||
|
@ -114,6 +121,21 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
|
||||
exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod);
|
||||
exportStaticMethods(cls, context.getDiagnostics());
|
||||
|
||||
if (typeHelper.isJavaScriptImplementation(cls.getName()) || !exposedClass.methods.isEmpty()) {
|
||||
cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName()));
|
||||
}
|
||||
|
||||
if (wasmGC && (!exposedClass.methods.isEmpty() || typeHelper.isJavaScriptClass(cls.getName()))) {
|
||||
var createWrapperMethod = new MethodHolder(JSMethods.MARSHALL_TO_JS);
|
||||
createWrapperMethod.setLevel(AccessLevel.PUBLIC);
|
||||
createWrapperMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||
cls.addMethod(createWrapperMethod);
|
||||
|
||||
if (typeHelper.isJavaScriptImplementation(cls.getName())) {
|
||||
cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void exposeMethods(ClassHolder classHolder, ExposedClass classToExpose, Diagnostics diagnostics,
|
||||
|
@ -129,14 +151,20 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
if (export.vararg) {
|
||||
--paramCount;
|
||||
}
|
||||
var exportedMethodSignature = new ValueType[paramCount + 1];
|
||||
var exportedMethodSignature = new ValueType[paramCount + 2];
|
||||
Arrays.fill(exportedMethodSignature, JSMethods.JS_OBJECT);
|
||||
if (methodRef.getReturnType() == ValueType.VOID) {
|
||||
exportedMethodSignature[exportedMethodSignature.length - 1] = ValueType.VOID;
|
||||
}
|
||||
MethodDescriptor exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
||||
exportedMethodSignature);
|
||||
MethodHolder exportedMethod = new MethodHolder(exportedMethodDesc);
|
||||
exportedMethod.getModifiers().add(ElementModifier.STATIC);
|
||||
exportedMethod.getAnnotations().add(new AnnotationHolder(JSInstanceExpose.class.getName()));
|
||||
Program program = new Program();
|
||||
exportedMethod.setProgram(program);
|
||||
program.createVariable();
|
||||
program.createVariable();
|
||||
|
||||
BasicBlock basicBlock = program.createBasicBlock();
|
||||
List<Instruction> marshallInstructions = new ArrayList<>();
|
||||
|
@ -161,9 +189,24 @@ 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);
|
||||
|
||||
var castInstance = new CastInstruction();
|
||||
castInstance.setValue(unmarshalledInstance.getReceiver());
|
||||
castInstance.setReceiver(program.createVariable());
|
||||
castInstance.setWeak(true);
|
||||
castInstance.setTargetType(ValueType.object(classHolder.getName()));
|
||||
basicBlock.add(castInstance);
|
||||
|
||||
InvokeInstruction invocation = new InvokeInstruction();
|
||||
invocation.setType(InvocationType.VIRTUAL);
|
||||
invocation.setInstance(program.variableAt(0));
|
||||
invocation.setType(method.getName().equals("<init>") ? InvocationType.SPECIAL : InvocationType.VIRTUAL);
|
||||
invocation.setInstance(castInstance.getReceiver());
|
||||
invocation.setMethod(methodRef);
|
||||
invocation.setArguments(variablesToPass);
|
||||
basicBlock.add(invocation);
|
||||
|
|
|
@ -24,12 +24,12 @@ import org.teavm.model.ClassReaderSource;
|
|||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class JSTypeHelper {
|
||||
public class JSTypeHelper {
|
||||
private ClassReaderSource classSource;
|
||||
private Map<String, Boolean> knownJavaScriptClasses = new HashMap<>();
|
||||
private Map<String, Boolean> knownJavaScriptImplementations = new HashMap<>();
|
||||
|
||||
JSTypeHelper(ClassReaderSource classSource) {
|
||||
public JSTypeHelper(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
knownJavaScriptClasses.put(JSObject.class.getName(), true);
|
||||
}
|
||||
|
|
|
@ -26,11 +26,14 @@ import org.teavm.model.instructions.InvocationType;
|
|||
class JSTypeInference extends BaseTypeInference<JSType> {
|
||||
private JSTypeHelper typeHelper;
|
||||
private ClassReaderSource classes;
|
||||
private boolean wasmGC;
|
||||
|
||||
JSTypeInference(JSTypeHelper typeHelper, ClassReaderSource classes, Program program, MethodReference reference) {
|
||||
JSTypeInference(JSTypeHelper typeHelper, ClassReaderSource classes, Program program, MethodReference reference,
|
||||
boolean wasmGC) {
|
||||
super(program, reference);
|
||||
this.typeHelper = typeHelper;
|
||||
this.classes = classes;
|
||||
this.wasmGC = wasmGC;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,7 +96,7 @@ class JSTypeInference extends BaseTypeInference<JSType> {
|
|||
if (!methodRef.getReturnType().isObject(Object.class)) {
|
||||
return mapType(methodRef.getReturnType());
|
||||
}
|
||||
return isJsMethod(methodRef) ? JSType.MIXED : JSType.JAVA;
|
||||
return !wasmGC && isJsMethod(methodRef) ? JSType.MIXED : JSType.JAVA;
|
||||
}
|
||||
|
||||
private boolean isJsMethod(MethodReference methodRef) {
|
||||
|
|
|
@ -87,6 +87,16 @@ class JSValueMarshaller {
|
|||
diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
|
||||
return var;
|
||||
}
|
||||
|
||||
var unwrapNative = new InvokeInstruction();
|
||||
unwrapNative.setLocation(location.getSourceLocation());
|
||||
unwrapNative.setType(InvocationType.SPECIAL);
|
||||
unwrapNative.setMethod(JSMethods.UNWRAP);
|
||||
unwrapNative.setArguments(var);
|
||||
unwrapNative.setReceiver(program.createVariable());
|
||||
replacement.add(unwrapNative);
|
||||
var = unwrapNative.getReceiver();
|
||||
|
||||
String name = type.getMethods().stream()
|
||||
.filter(method -> method.hasModifier(ElementModifier.ABSTRACT))
|
||||
.findFirst().get().getName();
|
||||
|
@ -142,6 +152,16 @@ class JSValueMarshaller {
|
|||
replacement.add(unwrapNative);
|
||||
return unwrapNative.getReceiver();
|
||||
}
|
||||
if (typeHelper.isJavaScriptClass(className) && jsType == JSType.JAVA) {
|
||||
var unwrapNative = new InvokeInstruction();
|
||||
unwrapNative.setLocation(location);
|
||||
unwrapNative.setType(InvocationType.SPECIAL);
|
||||
unwrapNative.setMethod(JSMethods.UNWRAP);
|
||||
unwrapNative.setArguments(var);
|
||||
unwrapNative.setReceiver(program.createVariable());
|
||||
replacement.add(unwrapNative);
|
||||
return unwrapNative.getReceiver();
|
||||
}
|
||||
return var;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.jso.impl;
|
||||
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSClass;
|
||||
|
@ -30,60 +31,61 @@ import org.teavm.jso.core.JSWeakMap;
|
|||
import org.teavm.jso.core.JSWeakRef;
|
||||
|
||||
public final class JSWrapper {
|
||||
private static final JSWeakMap<JSObject, JSTransparentInt> hashCodes = new JSWeakMap<>();
|
||||
private static final JSWeakMap<JSObject, JSWeakRef<JSObject>> wrappers = JSWeakRef.isSupported()
|
||||
? new JSWeakMap<>() : null;
|
||||
private static final JSMap<JSString, JSWeakRef<JSObject>> stringWrappers = JSWeakRef.isSupported()
|
||||
? new JSMap<>() : null;
|
||||
private static final JSMap<JSNumber, JSWeakRef<JSObject>> numberWrappers = JSWeakRef.isSupported()
|
||||
? new JSMap<>() : null;
|
||||
private static JSWeakRef<JSObject> undefinedWrapper;
|
||||
private static final JSFinalizationRegistry stringFinalizationRegistry;
|
||||
private static final JSFinalizationRegistry numberFinalizationRegistry;
|
||||
private static int hashCodeGen;
|
||||
private static class Helper {
|
||||
private static final JSWeakMap<JSObject, JSTransparentInt> hashCodes = new JSWeakMap<>();
|
||||
private static final JSWeakMap<JSObject, JSWeakRef<JSObject>> wrappers = JSWeakRef.isSupported()
|
||||
? new JSWeakMap<>() : null;
|
||||
private static final JSMap<JSString, JSWeakRef<JSObject>> stringWrappers = JSWeakRef.isSupported()
|
||||
? new JSMap<>() : null;
|
||||
private static final JSMap<JSNumber, JSWeakRef<JSObject>> numberWrappers = JSWeakRef.isSupported()
|
||||
? new JSMap<>() : null;
|
||||
private static JSWeakRef<JSObject> undefinedWrapper;
|
||||
private static JSFinalizationRegistry stringFinalizationRegistry;
|
||||
private static JSFinalizationRegistry numberFinalizationRegistry;
|
||||
private static int hashCodeGen;
|
||||
|
||||
static {
|
||||
stringFinalizationRegistry = stringWrappers != null
|
||||
? new JSFinalizationRegistry(token -> stringWrappers.delete((JSString) token))
|
||||
: null;
|
||||
numberFinalizationRegistry = numberWrappers != null
|
||||
? new JSFinalizationRegistry(token -> numberWrappers.delete((JSNumber) token))
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
public final JSObject js;
|
||||
|
||||
static {
|
||||
stringFinalizationRegistry = stringWrappers != null
|
||||
? new JSFinalizationRegistry(token -> stringWrappers.delete((JSString) token))
|
||||
: null;
|
||||
numberFinalizationRegistry = numberWrappers != null
|
||||
? new JSFinalizationRegistry(token -> numberWrappers.delete((JSNumber) token))
|
||||
: null;
|
||||
}
|
||||
|
||||
private JSWrapper(JSObject js) {
|
||||
this.js = js;
|
||||
}
|
||||
|
||||
public static Object wrap(Object o) {
|
||||
public static Object wrap(JSObject o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
var js = directJavaToJs(o);
|
||||
var type = JSObjects.typeOf(js);
|
||||
var type = JSObjects.typeOf(o);
|
||||
var isObject = type.equals("object") || type.equals("function");
|
||||
if (isObject && isJSImplementation(o)) {
|
||||
return o;
|
||||
}
|
||||
var wrappers = Helper.wrappers;
|
||||
if (wrappers != null) {
|
||||
if (isObject) {
|
||||
var existingRef = get(wrappers, js);
|
||||
var existingRef = get(wrappers, o);
|
||||
var existing = !isUndefined(existingRef) ? deref(existingRef) : JSUndefined.instance();
|
||||
if (isUndefined(existing)) {
|
||||
var wrapper = new JSWrapper(js);
|
||||
set(wrappers, js, createWeakRef(wrapperToJs(wrapper)));
|
||||
var wrapper = new JSWrapper(o);
|
||||
set(wrappers, o, createWeakRef(wrapperToJs(wrapper)));
|
||||
return wrapper;
|
||||
} else {
|
||||
return jsToWrapper(existing);
|
||||
}
|
||||
} else if (type.equals("string")) {
|
||||
var jsString = (JSString) js;
|
||||
var jsString = (JSString) o;
|
||||
var stringWrappers = Helper.stringWrappers;
|
||||
var stringFinalizationRegistry = Helper.stringFinalizationRegistry;
|
||||
var existingRef = get(stringWrappers, jsString);
|
||||
var existing = !isUndefined(existingRef) ? deref(existingRef) : JSUndefined.instance();
|
||||
if (isUndefined(existing)) {
|
||||
var wrapper = new JSWrapper(js);
|
||||
var wrapper = new JSWrapper(o);
|
||||
var wrapperAsJs = wrapperToJs(wrapper);
|
||||
set(stringWrappers, jsString, createWeakRef(wrapperAsJs));
|
||||
register(stringFinalizationRegistry, wrapperAsJs, jsString);
|
||||
|
@ -92,11 +94,13 @@ public final class JSWrapper {
|
|||
return jsToWrapper(existing);
|
||||
}
|
||||
} else if (type.equals("number")) {
|
||||
var jsNumber = (JSNumber) js;
|
||||
var jsNumber = (JSNumber) o;
|
||||
var numberWrappers = Helper.numberWrappers;
|
||||
var numberFinalizationRegistry = Helper.numberFinalizationRegistry;
|
||||
var existingRef = get(numberWrappers, jsNumber);
|
||||
var existing = !isUndefined(existingRef) ? deref(existingRef) : JSUndefined.instance();
|
||||
if (isUndefined(existing)) {
|
||||
var wrapper = new JSWrapper(js);
|
||||
var wrapper = new JSWrapper(o);
|
||||
var wrapperAsJs = wrapperToJs(wrapper);
|
||||
set(numberWrappers, jsNumber, createWeakRef(wrapperAsJs));
|
||||
register(numberFinalizationRegistry, wrapperAsJs, jsNumber);
|
||||
|
@ -105,19 +109,19 @@ public final class JSWrapper {
|
|||
return jsToWrapper(existing);
|
||||
}
|
||||
} else if (type.equals("undefined")) {
|
||||
var existingRef = undefinedWrapper;
|
||||
var existingRef = Helper.undefinedWrapper;
|
||||
var existing = existingRef != null ? deref(existingRef) : JSUndefined.instance();
|
||||
if (isUndefined(existing)) {
|
||||
var wrapper = new JSWrapper(js);
|
||||
var wrapper = new JSWrapper(o);
|
||||
var wrapperAsJs = wrapperToJs(wrapper);
|
||||
undefinedWrapper = createWeakRef(wrapperAsJs);
|
||||
Helper.undefinedWrapper = createWeakRef(wrapperAsJs);
|
||||
return wrapper;
|
||||
} else {
|
||||
return jsToWrapper(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new JSWrapper(js);
|
||||
return new JSWrapper(o);
|
||||
}
|
||||
|
||||
@JSBody(params = "target", script = "return new WeakRef(target);")
|
||||
|
@ -152,14 +156,18 @@ public final class JSWrapper {
|
|||
|
||||
@NoSideEffects
|
||||
public static Object maybeWrap(Object o) {
|
||||
return o == null || isJava(o) ? o : wrap(o);
|
||||
return o == null || isJava(o) ? o : wrap(directJavaToJs(o));
|
||||
}
|
||||
|
||||
@NoSideEffects
|
||||
public static native JSObject directJavaToJs(Object obj);
|
||||
|
||||
@NoSideEffects
|
||||
public static native Object directJsToJava(JSObject obj);
|
||||
public static native JSObject marshallJavaToJs(Object obj);
|
||||
|
||||
@NoSideEffects
|
||||
@Import(name = "unwrapJavaObject", module = "teavmJso")
|
||||
public static native Object unmarshallJavaFromJs(JSObject obj);
|
||||
|
||||
@NoSideEffects
|
||||
public static native JSObject dependencyJavaToJs(Object obj);
|
||||
|
@ -186,14 +194,14 @@ public final class JSWrapper {
|
|||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
return isJSImplementation(o) ? directJavaToJs(o) : ((JSWrapper) o).js;
|
||||
return isJSImplementation(o) ? marshallJavaToJs(o) : ((JSWrapper) o).js;
|
||||
}
|
||||
|
||||
public static JSObject maybeUnwrap(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
return isJava(o) ? unwrap(o) : directJavaToJs(o);
|
||||
return isJava(o) ? unwrap(o) : marshallJavaToJs(o);
|
||||
}
|
||||
|
||||
public static JSObject javaToJs(Object o) {
|
||||
|
@ -207,7 +215,7 @@ public final class JSWrapper {
|
|||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
return !isJava(o) ? wrap(directJsToJava(o)) : dependencyJsToJava(o);
|
||||
return !isJava(o) ? wrap(o) : dependencyJsToJava(o);
|
||||
}
|
||||
|
||||
public static boolean isJs(Object o) {
|
||||
|
@ -229,10 +237,10 @@ public final class JSWrapper {
|
|||
public int hashCode() {
|
||||
var type = JSObjects.typeOf(js);
|
||||
if (type.equals("object") || type.equals("symbol") || type.equals("function")) {
|
||||
var code = hashCodes.get(js);
|
||||
var code = Helper.hashCodes.get(js);
|
||||
if (isUndefined(code)) {
|
||||
code = JSTransparentInt.valueOf(++hashCodeGen);
|
||||
hashCodes.set(js, code);
|
||||
code = JSTransparentInt.valueOf(++Helper.hashCodeGen);
|
||||
Helper.hashCodes.set(js, code);
|
||||
}
|
||||
return code.intValue();
|
||||
} else if (type.equals("number")) {
|
||||
|
|
|
@ -15,33 +15,43 @@
|
|||
*/
|
||||
package org.teavm.jso.impl;
|
||||
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
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 {
|
||||
public class JSWrapperDependency extends AbstractDependencyListener {
|
||||
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;
|
||||
public void started(DependencyAgent agent) {
|
||||
externalClassesNode = agent.createNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classReached(DependencyAgent agent, String className) {
|
||||
var cls = agent.getClassSource().get(className);
|
||||
if (cls.getAnnotations().get(JSClassToExpose.class.getName()) != null) {
|
||||
externalClassesNode.propagate(agent.getType(className));
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyNode getExternalClassesNode(DependencyAgent agent) {
|
||||
if (externalClassesNode == null) {
|
||||
externalClassesNode = agent.createNode();
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getMethod().getOwnerName().equals(JSWrapper.class.getName())) {
|
||||
switch (method.getMethod().getName()) {
|
||||
case "jsToWrapper":
|
||||
method.getResult().propagate(agent.getType(JSWrapper.class.getName()));
|
||||
break;
|
||||
case "dependencyJavaToJs":
|
||||
case "marshallJavaToJs":
|
||||
method.getVariable(1).connect(externalClassesNode);
|
||||
break;
|
||||
case "dependencyJsToJava":
|
||||
case "unmarshallJavaFromJs":
|
||||
externalClassesNode.connect(method.getResult());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return externalClassesNode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ public class JSWrapperGenerator implements Injector, DependencyPlugin {
|
|||
case "directJsToJava":
|
||||
case "dependencyJavaToJs":
|
||||
case "dependencyJsToJava":
|
||||
case "marshallJavaToJs":
|
||||
case "unmarshallJavaFromJs":
|
||||
case "wrapperToJs":
|
||||
case "jsToWrapper":
|
||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||
|
|
|
@ -20,35 +20,42 @@ 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.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.jso.impl.JSBodyEmitter;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class WasmGCBodyIntrinsic implements WasmGCIntrinsic {
|
||||
private JSBodyEmitter emitter;
|
||||
private boolean inlined;
|
||||
private WasmGCBodyGenerator bodyGenerator;
|
||||
private WasmGCJsoCommonGenerator commonGen;
|
||||
private WasmGlobal global;
|
||||
private WasmGCJSFunctions jsFunctions;
|
||||
|
||||
WasmGCBodyIntrinsic(JSBodyEmitter emitter, boolean inlined, WasmGCBodyGenerator bodyGenerator,
|
||||
WasmGCBodyIntrinsic(JSBodyEmitter emitter, boolean inlined, WasmGCJsoCommonGenerator commonGen,
|
||||
WasmGCJSFunctions jsFunctions) {
|
||||
this.emitter = emitter;
|
||||
this.inlined = inlined;
|
||||
this.bodyGenerator = bodyGenerator;
|
||||
this.commonGen = commonGen;
|
||||
this.jsFunctions = jsFunctions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
var jsoContext = WasmGCJsoContext.wrap(context);
|
||||
if (global == null) {
|
||||
global = bodyGenerator.addBody(context, emitter, inlined);
|
||||
global = commonGen.addJSBody(jsoContext, emitter, inlined);
|
||||
}
|
||||
var call = new WasmCall(jsFunctions.getFunctionCaller(context, invocation.getArguments().size()));
|
||||
var call = new WasmCall(jsFunctions.getFunctionCaller(jsoContext, invocation.getArguments().size()));
|
||||
call.getArguments().add(new WasmGetGlobal(global));
|
||||
for (var arg : invocation.getArguments()) {
|
||||
call.getArguments().add(context.generate(arg));
|
||||
}
|
||||
return call;
|
||||
WasmExpression result = call;
|
||||
if (invocation.getMethod().getReturnType() == ValueType.VOID) {
|
||||
result = new WasmDrop(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,12 +24,13 @@ import org.teavm.model.MethodReference;
|
|||
class WasmGCJSBodyRenderer implements WasmGCIntrinsicFactory {
|
||||
private JSBodyRepository repository;
|
||||
private WasmGCJSFunctions jsFunctions;
|
||||
private WasmGCBodyGenerator bodyGenerator;
|
||||
private WasmGCJsoCommonGenerator commonGen;
|
||||
|
||||
WasmGCJSBodyRenderer(JSBodyRepository repository) {
|
||||
WasmGCJSBodyRenderer(JSBodyRepository repository, WasmGCJSFunctions jsFunctions,
|
||||
WasmGCJsoCommonGenerator commonGen) {
|
||||
this.repository = repository;
|
||||
jsFunctions = new WasmGCJSFunctions();
|
||||
bodyGenerator = new WasmGCBodyGenerator(jsFunctions);
|
||||
this.jsFunctions = jsFunctions;
|
||||
this.commonGen = commonGen;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,6 +40,6 @@ class WasmGCJSBodyRenderer implements WasmGCIntrinsicFactory {
|
|||
return null;
|
||||
}
|
||||
var inlined = repository.inlineMethods.contains(emitter.method());
|
||||
return new WasmGCBodyIntrinsic(emitter, inlined, bodyGenerator, jsFunctions);
|
||||
return new WasmGCBodyIntrinsic(emitter, inlined, commonGen, jsFunctions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
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;
|
||||
|
||||
|
@ -24,7 +23,7 @@ class WasmGCJSFunctions {
|
|||
private WasmFunction[] constructors = new WasmFunction[32];
|
||||
private WasmFunction[] callers = new WasmFunction[32];
|
||||
|
||||
WasmFunction getFunctionConstructor(WasmGCIntrinsicContext context, int index) {
|
||||
WasmFunction getFunctionConstructor(WasmGCJsoContext context, int index) {
|
||||
var function = constructors[index];
|
||||
if (function == null) {
|
||||
var extern = WasmType.SpecialReferenceKind.EXTERN.asNonNullType();
|
||||
|
@ -41,7 +40,7 @@ class WasmGCJSFunctions {
|
|||
return function;
|
||||
}
|
||||
|
||||
WasmFunction getFunctionCaller(WasmGCIntrinsicContext context, int index) {
|
||||
WasmFunction getFunctionCaller(WasmGCJsoContext context, int index) {
|
||||
var function = callers[index];
|
||||
if (function == null) {
|
||||
var paramTypes = new WasmType[index + 1];
|
||||
|
|
|
@ -19,14 +19,14 @@ 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;
|
||||
import org.teavm.jso.core.JSArray;
|
||||
import org.teavm.jso.impl.JSTypeHelper;
|
||||
|
||||
class WasmGCJSTypeMapper implements WasmGCCustomTypeMapper, WasmGCCustomTypeMapperFactory {
|
||||
private JSTypeHelper typeHelper;
|
||||
|
||||
@Override
|
||||
public WasmType map(String className) {
|
||||
if (className.equals(JSObject.class.getName())
|
||||
|| className.equals(JSArray.class.getName())) {
|
||||
if (typeHelper.isJavaScriptClass(className)) {
|
||||
return WasmType.Reference.EXTERN;
|
||||
}
|
||||
return null;
|
||||
|
@ -34,6 +34,7 @@ class WasmGCJSTypeMapper implements WasmGCCustomTypeMapper, WasmGCCustomTypeMapp
|
|||
|
||||
@Override
|
||||
public WasmGCCustomTypeMapper createTypeMapper(WasmGCCustomTypeMapperFactoryContext context) {
|
||||
this.typeHelper = new JSTypeHelper(context.originalClasses());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.jso.JSObject;
|
||||
import org.teavm.jso.impl.JSMarshallable;
|
||||
import org.teavm.jso.impl.JSWrapper;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
|
||||
class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
if (cls.getName().equals(JSWrapper.class.getName())) {
|
||||
transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class,
|
||||
JSObject.class)), context);
|
||||
transformIsJsImplementation(cls.getMethod(new MethodDescriptor("isJSImplementation",
|
||||
Object.class, boolean.class)), context);
|
||||
}
|
||||
}
|
||||
|
||||
private void transformMarshallMethod(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
var obj = pe.var(1, Object.class);
|
||||
obj.cast(JSMarshallable.class).invokeVirtual("marshallToJs", JSObject.class).returnValue();
|
||||
}
|
||||
|
||||
private void transformIsJsImplementation(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
var obj = pe.var(1, JSObject.class);
|
||||
obj.instanceOf(ValueType.parse(JSMarshallable.class)).returnValue();
|
||||
}
|
||||
}
|
|
@ -28,8 +28,12 @@ public final class WasmGCJso {
|
|||
|
||||
public static void install(TeaVMHost host, TeaVMWasmGCHost wasmGCHost, JSBodyRepository jsBodyRepository) {
|
||||
host.add(new WasmGCJSDependencies());
|
||||
host.add(new WasmGCJSWrapperTransformer());
|
||||
var jsFunctions = new WasmGCJSFunctions();
|
||||
var commonGen = new WasmGCJsoCommonGenerator(jsFunctions);
|
||||
wasmGCHost.addCustomTypeMapperFactory(new WasmGCJSTypeMapper());
|
||||
wasmGCHost.addIntrinsicFactory(new WasmGCJSBodyRenderer(jsBodyRepository));
|
||||
wasmGCHost.addIntrinsicFactory(new WasmGCJSBodyRenderer(jsBodyRepository, jsFunctions, commonGen));
|
||||
wasmGCHost.addGeneratorFactory(new WasmGCMarshallMethodGeneratorFactory(commonGen));
|
||||
|
||||
var jsIntrinsic = new WasmGCJSIntrinsic();
|
||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "wrap", String.class, JSObject.class), jsIntrinsic);
|
||||
|
|
|
@ -20,11 +20,11 @@ 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.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
|
@ -32,16 +32,16 @@ import org.teavm.jso.impl.JSBodyAstEmitter;
|
|||
import org.teavm.jso.impl.JSBodyBloatedEmitter;
|
||||
import org.teavm.jso.impl.JSBodyEmitter;
|
||||
|
||||
class WasmGCBodyGenerator {
|
||||
class WasmGCJsoCommonGenerator {
|
||||
private WasmGCJSFunctions jsFunctions;
|
||||
private boolean initialized;
|
||||
private List<Consumer<WasmFunction>> initializerParts = new ArrayList<>();
|
||||
|
||||
WasmGCBodyGenerator(WasmGCJSFunctions jsFunctions) {
|
||||
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
|
||||
this.jsFunctions = jsFunctions;
|
||||
}
|
||||
|
||||
private void initialize(WasmGCIntrinsicContext context) {
|
||||
private void initialize(WasmGCJsoContext context) {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
@ -49,14 +49,17 @@ class WasmGCBodyGenerator {
|
|||
context.addToInitializer(this::writeToInitializer);
|
||||
}
|
||||
|
||||
|
||||
private void writeToInitializer(WasmFunction function) {
|
||||
for (var part : initializerParts) {
|
||||
part.accept(function);
|
||||
}
|
||||
}
|
||||
|
||||
WasmGlobal addBody(WasmGCIntrinsicContext context, JSBodyEmitter emitter, boolean inlined) {
|
||||
void addInitializerPart(Consumer<WasmFunction> part) {
|
||||
initializerParts.add(part);
|
||||
}
|
||||
|
||||
WasmGlobal addJSBody(WasmGCJsoContext context, JSBodyEmitter emitter, boolean inlined) {
|
||||
initialize(context);
|
||||
var paramCount = emitter.method().parameterCount();
|
||||
if (!emitter.isStatic()) {
|
||||
|
@ -87,9 +90,7 @@ class WasmGCBodyGenerator {
|
|||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
var constructor = new WasmCall(jsFunctions.getFunctionConstructor(context,
|
||||
paramCount));
|
||||
var stringToJs = context.functions().forStaticMethod(STRING_TO_JS);
|
||||
var constructor = new WasmCall(jsFunctions.getFunctionConstructor(context, paramCount));
|
||||
var paramNames = new ArrayList<String>();
|
||||
if (!emitter.isStatic()) {
|
||||
paramNames.add("__this__");
|
||||
|
@ -97,12 +98,20 @@ class WasmGCBodyGenerator {
|
|||
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));
|
||||
constructor.getArguments().add(stringToJs(context, paramName));
|
||||
}
|
||||
var functionBody = new WasmGetGlobal(context.strings().getStringConstant(body).global);
|
||||
constructor.getArguments().add(new WasmCall(stringToJs, functionBody));
|
||||
constructor.getArguments().add(stringToJs(context, functionBody));
|
||||
initializerParts.add(initializer -> initializer.getBody().add(new WasmSetGlobal(global, constructor)));
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
private WasmFunction stringToJsFunction(WasmGCJsoContext context) {
|
||||
return context.functions().forStaticMethod(STRING_TO_JS);
|
||||
}
|
||||
|
||||
WasmExpression stringToJs(WasmGCJsoContext context, WasmExpression str) {
|
||||
return new WasmCall(stringToJsFunction(context), str);
|
||||
}
|
||||
}
|
|
@ -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 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.strings.WasmGCStringProvider;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
|
||||
interface WasmGCJsoContext {
|
||||
WasmModule module();
|
||||
|
||||
WasmFunctionTypes functionTypes();
|
||||
|
||||
BaseWasmFunctionRepository functions();
|
||||
|
||||
WasmGCNameProvider names();
|
||||
|
||||
WasmGCStringProvider strings();
|
||||
|
||||
void addToInitializer(Consumer<WasmFunction> initializerContributor);
|
||||
|
||||
static WasmGCJsoContext wrap(WasmGCIntrinsicContext context) {
|
||||
return new WasmGCJsoContext() {
|
||||
@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 void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static WasmGCJsoContext wrap(WasmGCCustomGeneratorContext context) {
|
||||
return new WasmGCJsoContext() {
|
||||
@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 void addToInitializer(Consumer<WasmFunction> initializerContributor) {
|
||||
context.addToInitializer(initializerContributor);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
|
||||
var jsoContext = WasmGCJsoContext.wrap(context);
|
||||
|
||||
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 wrapperFunction = javaObjectToJSFunction(context);
|
||||
function.getBody().add(new WasmCall(wrapperFunction, new WasmGetLocal(thisLocal),
|
||||
new WasmGetGlobal(jsClassGlobal)));
|
||||
}
|
||||
|
||||
private WasmFunction javaObjectToJSFunction(WasmGCCustomGeneratorContext context) {
|
||||
if (javaObjectToJSFunction == null) {
|
||||
javaObjectToJSFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
||||
context.typeMapper().mapType(ValueType.parse(Object.class)), WasmType.Reference.EXTERN));
|
||||
javaObjectToJSFunction.setName(context.names().topLevel("teavm.jso@javaObjectToJS"));
|
||||
javaObjectToJSFunction.setImportName("javaObjectToJS");
|
||||
javaObjectToJSFunction.setImportModule("teavmJso");
|
||||
context.module().functions.add(javaObjectToJSFunction);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactory;
|
||||
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactoryContext;
|
||||
import org.teavm.jso.impl.JSMethods;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
class WasmGCMarshallMethodGeneratorFactory implements WasmGCCustomGeneratorFactory {
|
||||
private WasmGCJsoCommonGenerator commonGen;
|
||||
|
||||
WasmGCMarshallMethodGeneratorFactory(WasmGCJsoCommonGenerator commonGen) {
|
||||
this.commonGen = commonGen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCCustomGenerator createGenerator(MethodReference methodRef,
|
||||
WasmGCCustomGeneratorFactoryContext context) {
|
||||
if (!methodRef.getName().equals(JSMethods.MARSHALL_TO_JS.getName())) {
|
||||
return null;
|
||||
}
|
||||
var cls = context.classes().get(methodRef.getClassName());
|
||||
return cls != null && cls.getInterfaces().contains(JSMethods.JS_MARSHALLABLE)
|
||||
? new WasmGCMarshallMethodGenerator(commonGen)
|
||||
: null;
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ import org.teavm.junit.TestPlatform;
|
|||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
@OnlyPlatform(TestPlatform.JAVASCRIPT)
|
||||
@OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
|
||||
@EachTestCompiledSeparately
|
||||
public class ExportClassTest {
|
||||
@Test
|
||||
|
|
|
@ -65,7 +65,7 @@ val generateLibJs by tasks.register<JavaExec>("generateLibJs") {
|
|||
}
|
||||
|
||||
val zipWithJs by tasks.register<Jar>("zipWithJs") {
|
||||
dependsOn(generateJs, generateLibJs)
|
||||
//dependsOn(generateJs, generateLibJs)
|
||||
archiveClassifier = "js"
|
||||
from(layout.buildDirectory.dir("teavm"), layout.buildDirectory.dir("teavm-lib"))
|
||||
entryCompression = ZipEntryCompression.DEFLATED
|
||||
|
|
Loading…
Reference in New Issue
Block a user