wasm gc: support resources

This commit is contained in:
Alexey Andreev 2024-09-24 08:46:04 +02:00
parent e8c939f40b
commit dd24425de0
29 changed files with 475 additions and 20 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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() {

View File

@ -0,0 +1,39 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.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()));
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
};
}

View File

@ -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;

View File

@ -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 {

View File

@ -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();
}

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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 "";