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