mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: support resources
This commit is contained in:
parent
e8c939f40b
commit
dd24425de0
|
@ -16,8 +16,8 @@
|
|||
package org.teavm.classlib.impl.console;
|
||||
|
||||
import org.teavm.backend.c.intrinsic.RuntimeInclude;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.WasmSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.classlib.PlatformDetector;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.Import;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.impl.console;
|
||||
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.classlib.PlatformDetector;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.impl.console;
|
||||
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.classlib.PlatformDetector;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.classlib.PlatformDetector;
|
||||
import org.teavm.dependency.PluggableDependency;
|
||||
import org.teavm.interop.Address;
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
|||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
|||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation;
|
||||
import org.teavm.backend.wasm.transformation.gc.ClassLoaderResourceTransformation;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.interop.Platforms;
|
||||
|
@ -119,7 +120,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
return List.of(
|
||||
new BaseClassesTransformation()
|
||||
new BaseClassesTransformation(),
|
||||
new ClassLoaderResourceTransformation()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -167,7 +169,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
|
||||
var module = new WasmModule();
|
||||
var customGenerators = new WasmGCCustomGenerators(classes, controller.getServices(),
|
||||
customGeneratorFactories, customCustomGenerators);
|
||||
customGeneratorFactories, customCustomGenerators,
|
||||
controller.getProperties());
|
||||
var intrinsics = new WasmGCIntrinsics(classes, controller.getServices(), intrinsicFactories, customIntrinsics);
|
||||
var declarationsGenerator = new WasmGCDeclarationsGenerator(
|
||||
module,
|
||||
|
@ -218,6 +221,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
}
|
||||
|
||||
moduleGenerator.generate();
|
||||
customGenerators.contributeToModule(module);
|
||||
adjustModuleMemory(module);
|
||||
|
||||
emitWasmFile(module, buildTarget, outputName);
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.teavm.backend.wasm.gc;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
|
@ -37,6 +37,7 @@ public class WasmGCDependencies {
|
|||
contributeInitializerUtils();
|
||||
contributeString();
|
||||
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
||||
analyzer.addDependencyListener(new WasmGCResourceDependency());
|
||||
}
|
||||
|
||||
public void contributeStandardExports() {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.gc;
|
||||
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmGCResourceDependency extends AbstractDependencyListener {
|
||||
private static final MethodReference ACQUIRE_METHOD = new MethodReference(WasmGCResources.class,
|
||||
"acquireResources", WasmGCResources.Resource[].class);
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getMethod().getReference().equals(ACQUIRE_METHOD)) {
|
||||
var create = agent.linkMethod(new MethodReference(WasmGCResources.class,
|
||||
"create", String.class, int.class, int.class, WasmGCResources.Resource.class));
|
||||
create.propagate(1, agent.getType("java.lang.String"));
|
||||
create.use();
|
||||
method.getResult().propagate(agent.getType("[" + WasmGCResources.Resource.class.getName()));
|
||||
method.getResult().getArrayItem().propagate(agent.getType(WasmGCResources.Resource.class.getName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNew;
|
|||
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReader;
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
|||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.teavm.model.CallLocation;
|
|||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.ListableClassHolderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -353,6 +354,16 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
}
|
||||
|
||||
private WasmGCCustomGeneratorContext customGeneratorContext = new WasmGCCustomGeneratorContext() {
|
||||
@Override
|
||||
public ClassLoader classLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListableClassReaderSource classes() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmModule module() {
|
||||
return module;
|
||||
|
@ -387,5 +398,15 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
public BaseWasmFunctionRepository functions() {
|
||||
return WasmGCMethodGenerator.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diagnostics diagnostics() {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCStringProvider strings() {
|
||||
return context.strings();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
|||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
|||
import org.teavm.backend.wasm.model.expression.WasmIsNull;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class ClassGenerators implements WasmGCCustomGenerator {
|
||||
|
|
|
@ -20,10 +20,17 @@ import org.teavm.backend.wasm.WasmFunctionTypes;
|
|||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
|
||||
public interface WasmGCCustomGeneratorContext {
|
||||
ClassLoader classLoader();
|
||||
|
||||
ListableClassReaderSource classes();
|
||||
|
||||
WasmModule module();
|
||||
|
||||
WasmFunctionTypes functionTypes();
|
||||
|
@ -37,4 +44,8 @@ public interface WasmGCCustomGeneratorContext {
|
|||
BaseWasmFunctionRepository functions();
|
||||
|
||||
WasmTag exceptionTag();
|
||||
|
||||
Diagnostics diagnostics();
|
||||
|
||||
WasmGCStringProvider strings();
|
||||
}
|
||||
|
|
|
@ -21,8 +21,11 @@ import java.lang.reflect.Array;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -32,24 +35,32 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
private Map<MethodReference, Container> generators = new HashMap<>();
|
||||
private ClassReaderSource classes;
|
||||
private ServiceRepository services;
|
||||
private WasmGCResourcesGenerator resourcesGenerator;
|
||||
|
||||
public WasmGCCustomGenerators(ClassReaderSource classes, ServiceRepository services,
|
||||
List<WasmGCCustomGeneratorFactory> factories,
|
||||
Map<MethodReference, WasmGCCustomGenerator> generators) {
|
||||
Map<MethodReference, WasmGCCustomGenerator> generators,
|
||||
Properties properties) {
|
||||
this.factories = List.copyOf(factories);
|
||||
this.classes = classes;
|
||||
this.services = services;
|
||||
resourcesGenerator = new WasmGCResourcesGenerator(properties);
|
||||
fillClass();
|
||||
fillStringPool();
|
||||
fillSystem();
|
||||
fillArray();
|
||||
fillWeakReference();
|
||||
fillString();
|
||||
fillResources();
|
||||
for (var entry : generators.entrySet()) {
|
||||
add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void contributeToModule(WasmModule module) {
|
||||
resourcesGenerator.writeModule(module);
|
||||
}
|
||||
|
||||
private void fillClass() {
|
||||
var classGenerators = new ClassGenerators();
|
||||
add(new MethodReference(Class.class, "isAssignableFrom", Class.class, boolean.class), classGenerators);
|
||||
|
@ -85,6 +96,11 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
add(new MethodReference(String.class, "intern", String.class), generator);
|
||||
}
|
||||
|
||||
private void fillResources() {
|
||||
add(new MethodReference(WasmGCResources.class, "acquireResources", WasmGCResources.Resource[].class),
|
||||
resourcesGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCCustomGenerator get(MethodReference method) {
|
||||
var result = generators.get(method);
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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.generators.gc;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil;
|
||||
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.WasmMemorySegment;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
||||
import org.teavm.classlib.ResourceSupplier;
|
||||
import org.teavm.classlib.ResourceSupplierContext;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class WasmGCResourcesGenerator implements WasmGCCustomGenerator {
|
||||
private Properties properties;
|
||||
private ByteArrayOutputStream resources = new ByteArrayOutputStream();
|
||||
private WasmGlobal baseGlobal;
|
||||
|
||||
public WasmGCResourcesGenerator(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public void writeModule(WasmModule module) {
|
||||
if (resources.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var segment = new WasmMemorySegment();
|
||||
if (!module.getSegments().isEmpty()) {
|
||||
var lastSegment = module.getSegments().get(module.getSegments().size() - 1);
|
||||
segment.setOffset(lastSegment.getOffset() + lastSegment.getLength());
|
||||
}
|
||||
segment.setData(resources.toByteArray());
|
||||
module.getSegments().add(segment);
|
||||
baseGlobal.setInitialValue(new WasmInt32Constant(segment.getOffset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
|
||||
var supplierContext = new SupplierContextImpl(context.classLoader(), context.classes(), properties);
|
||||
var resourceSet = new LinkedHashSet<String>();
|
||||
for (var supplier : ServiceLoader.load(ResourceSupplier.class, context.classLoader())) {
|
||||
var resources = supplier.supplyResources(supplierContext);
|
||||
if (resources != null) {
|
||||
resourceSet.addAll(Arrays.asList(resources));
|
||||
}
|
||||
}
|
||||
|
||||
var descriptors = new ArrayList<ResourceDescriptor>();
|
||||
var location = new CallLocation(new MethodReference(ClassLoader.class,
|
||||
"getResourceAsStream", String.class, InputStream.class));
|
||||
for (var resource : resourceSet) {
|
||||
try (var input = context.classLoader().getResourceAsStream(resource)) {
|
||||
if (input == null) {
|
||||
context.diagnostics().error(location, "Resource not found: " + resource);
|
||||
} else {
|
||||
var start = resources.size();
|
||||
input.transferTo(resources);
|
||||
var end = resources.size();
|
||||
descriptors.add(new ResourceDescriptor(resource, start, end));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
context.diagnostics().error(location, "Error occurred reading resource '" + resource + "'");
|
||||
}
|
||||
}
|
||||
|
||||
if (!descriptors.isEmpty()) {
|
||||
baseGlobal = new WasmGlobal(context.names().topLevel("teavm@resourcesBaseAddress"),
|
||||
WasmType.INT32, new WasmInt32Constant(0));
|
||||
context.module().globals.add(baseGlobal);
|
||||
|
||||
var genUtil = new WasmGCGenerationUtil(context.classInfoProvider(), new TemporaryVariablePool(function));
|
||||
var local = new WasmLocal(context.typeMapper().mapType(ValueType.parse(WasmGCResources.Resource[].class)));
|
||||
function.add(local);
|
||||
var constructor = context.functions().forStaticMethod(new MethodReference(WasmGCResources.class,
|
||||
"create", String.class, int.class, int.class, WasmGCResources.Resource.class));
|
||||
|
||||
genUtil.allocateArrayWithElements(
|
||||
ValueType.parse(WasmGCResources.Resource.class),
|
||||
() -> {
|
||||
var items = new ArrayList<WasmExpression>();
|
||||
for (var descriptor : descriptors) {
|
||||
var name = context.strings().getStringConstant(descriptor.name);
|
||||
var offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||
new WasmGetGlobal(baseGlobal), new WasmInt32Constant(descriptor.address));
|
||||
var end = new WasmInt32Constant(descriptor.end - descriptor.address);
|
||||
items.add(new WasmCall(constructor, new WasmGetGlobal(name.global), offset, end));
|
||||
}
|
||||
return items;
|
||||
},
|
||||
null,
|
||||
local,
|
||||
function.getBody()
|
||||
);
|
||||
function.getBody().add(new WasmGetLocal(local));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResourceDescriptor {
|
||||
String name;
|
||||
int address;
|
||||
int end;
|
||||
|
||||
ResourceDescriptor(String name, int address, int end) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
static class SupplierContextImpl implements ResourceSupplierContext {
|
||||
private ClassLoader classLoader;
|
||||
private ListableClassReaderSource classSource;
|
||||
private Properties properties;
|
||||
|
||||
SupplierContextImpl(ClassLoader classLoader, ListableClassReaderSource classSource,
|
||||
Properties properties) {
|
||||
this.classLoader = classLoader;
|
||||
this.classSource = classSource;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListableClassReaderSource getClassSource() {
|
||||
return classSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.teavm.backend.wasm.WasmRuntime;
|
|||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -49,6 +50,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
|||
fillDouble();
|
||||
fillArray();
|
||||
fillString();
|
||||
fillResources();
|
||||
for (var entry : customIntrinsics.entrySet()) {
|
||||
add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
@ -156,6 +158,11 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
|||
add(new MethodReference(className, "setValue", ValueType.parse(String.class), ValueType.VOID), intrinsic);
|
||||
}
|
||||
|
||||
private void fillResources() {
|
||||
var intrinsic = new WasmGCResourcesIntrinsic();
|
||||
add(new MethodReference(WasmGCResources.class, "readSingleByte", int.class, int.class), intrinsic);
|
||||
}
|
||||
|
||||
private void add(MethodReference methodRef, WasmGCIntrinsic intrinsic) {
|
||||
intrinsics.put(methodRef, new IntrinsicContainer(intrinsic));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2024 konsoletyper.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.intrinsics.gc;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
|
||||
public class WasmGCResourcesIntrinsic implements WasmGCIntrinsic {
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
return new WasmLoadInt32(1, context.generate(invocation.getArguments().get(0)), WasmInt32Subtype.UINT8);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.runtime.gc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class WasmGCResources {
|
||||
private static Map<String, Resource> resources = new HashMap<>();
|
||||
|
||||
private WasmGCResources() {
|
||||
}
|
||||
|
||||
static {
|
||||
for (var resource : acquireResources()) {
|
||||
resources.put(resource.name, resource);
|
||||
}
|
||||
}
|
||||
|
||||
private static native Resource[] acquireResources();
|
||||
|
||||
private static Resource create(String name, int start, int length) {
|
||||
return new Resource(name, start, start + length);
|
||||
}
|
||||
|
||||
public static InputStream getResource(String name) {
|
||||
var resource = resources.get(name);
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
return new ResourceInputStream(resource.start, resource.end);
|
||||
}
|
||||
|
||||
public static class Resource {
|
||||
public final String name;
|
||||
public final int start;
|
||||
public final int end;
|
||||
|
||||
public Resource(String name, int start, int end) {
|
||||
this.name = name;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ResourceInputStream extends InputStream {
|
||||
private int address;
|
||||
private int end;
|
||||
|
||||
private ResourceInputStream(int address, int end) {
|
||||
this.address = address;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return address < end ? readSingleByte(address++) : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (address >= end) {
|
||||
return -1;
|
||||
}
|
||||
var bytesToRead = Math.min(len, end - address);
|
||||
readBytes(b, off, bytesToRead, address);
|
||||
address += bytesToRead;
|
||||
return bytesToRead;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readBytes(byte[] bytes, int off, int len, int fromAddress) {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
bytes[i] = (byte) readSingleByte(fromAddress++);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int readSingleByte(int address);
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.runtime;
|
||||
package org.teavm.backend.wasm.runtime.gc;
|
||||
|
||||
import org.teavm.interop.Import;
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.transformation.gc;
|
||||
|
||||
import java.io.InputStream;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
|
||||
public class ClassLoaderResourceTransformation implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
if (cls.getName().equals(ClassLoader.class.getName())) {
|
||||
var method = cls.getMethod(new MethodDescriptor("getResourceAsStream", String.class, InputStream.class));
|
||||
transformGetResourceAsStream(method);
|
||||
}
|
||||
}
|
||||
|
||||
private void transformGetResourceAsStream(MethodHolder method) {
|
||||
var program = new Program();
|
||||
program.createVariable();
|
||||
var nameVar = program.createVariable();
|
||||
var block = program.createBasicBlock();
|
||||
|
||||
var invoke = new InvokeInstruction();
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
invoke.setMethod(new MethodReference(WasmGCResources.class, "getResource", String.class,
|
||||
InputStream.class));
|
||||
invoke.setArguments(nameVar);
|
||||
invoke.setReceiver(program.createVariable());
|
||||
block.add(invoke);
|
||||
|
||||
var exit = new ExitInstruction();
|
||||
exit.setValueToReturn(invoke.getReceiver());
|
||||
block.add(exit);
|
||||
|
||||
method.setProgram(program);
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNull;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.EachTestCompiledSeparately;
|
||||
|
@ -48,14 +48,14 @@ public class ClassLoaderTest {
|
|||
@Test
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
|
||||
public void returnsNullForNonExistentResource() {
|
||||
InputStream input = ClassLoader.getSystemClassLoader().getResourceAsStream("non-existent-resource.txt");
|
||||
var input = ClassLoader.getSystemClassLoader().getResourceAsStream("non-existent-resource.txt");
|
||||
assertNull(input);
|
||||
}
|
||||
|
||||
private static String loadResource(String name) {
|
||||
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream(
|
||||
"resources-for-test/" + name), "UTF-8"))) {
|
||||
var classLoader = ClassLoader.getSystemClassLoader();
|
||||
try (var reader = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream(
|
||||
"resources-for-test/" + name), StandardCharsets.UTF_8))) {
|
||||
return reader.readLine();
|
||||
} catch (IOException e) {
|
||||
return "";
|
||||
|
|
Loading…
Reference in New Issue
Block a user