wasm gc: implement some basic reflection-specific intrinsics in Object and Class

This commit is contained in:
Alexey Andreev 2024-08-13 20:51:20 +02:00
parent a84c5fc77f
commit eaf2c8189f
14 changed files with 242 additions and 9 deletions

View File

@ -53,7 +53,7 @@ import org.teavm.platform.PlatformSequence;
import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject;
public class TClass<T> extends TObject implements TAnnotatedElement, TType {
public final class TClass<T> extends TObject implements TAnnotatedElement, TType {
String name;
String simpleName;
String canonicalName;

View File

@ -26,6 +26,7 @@ import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation;
import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener;
import org.teavm.interop.Platforms;
@ -70,7 +71,9 @@ public class WasmGCTarget implements TeaVMTarget {
@Override
public List<ClassHolderTransformer> getTransformers() {
return List.of();
return List.of(
new BaseClassesTransformation()
);
}
@Override

View File

@ -70,6 +70,8 @@ import org.teavm.model.util.ReflectionUtil;
public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor {
private static final MethodDescriptor CLINIT_METHOD_DESC = new MethodDescriptor("<clinit>", ValueType.VOID);
private static final MethodDescriptor GET_CLASS_METHOD = new MethodDescriptor("getClass",
ValueType.parse(Class.class));
private final WasmModule module;
private ClassReaderSource classSource;
@ -231,10 +233,16 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return classTagOffset;
}
@Override
public int getClassArrayItemOffset() {
return classArrayItemOffset;
}
@Override
public int getClassSupertypeFunctionOffset() {
return classSupertypeFunctionOffset;
}
@Override
public int getVirtualMethodsOffset() {
return virtualTableFieldOffset;
@ -330,13 +338,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) {
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
filled);
}
for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method);
if (entry != null && entry.getImplementor() != null && filled.add(method)) {
if (entry != null && entry.getImplementor() != null && filled.add(method)
&& !method.equals(GET_CLASS_METHOD)) {
var function = functionProvider.forInstanceMethod(entry.getImplementor());
if (!origin.equals(entry.getImplementor().getClassName())) {
var functionType = getFunctionType(virtualTable.getClassName(), method);
@ -356,9 +361,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
function.setReferenced(true);
var ref = new WasmFunctionReference(function);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index + entry.getIndex(), ref));
}
++index;
}
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
filled);
}
return index;
}

View File

@ -33,6 +33,10 @@ public interface WasmGCClassInfoProvider {
int getVirtualMethodsOffset();
int getClassArrayItemOffset();
int getClassSupertypeFunctionOffset();
default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name));
}

View File

@ -582,5 +582,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
public WasmGCTypeMapper typeMapper() {
return context.typeMapper();
}
@Override
public WasmGCClassInfoProvider classInfoProvider() {
return context.classInfoProvider();
}
};
}

View File

@ -335,5 +335,10 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
public WasmGCTypeMapper typeMapper() {
return typeMapper;
}
@Override
public WasmGCClassInfoProvider classInfoProvider() {
return classInfoProvider;
}
};
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generators.gc;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.model.MethodReference;
public class ClassGenerators implements WasmGCCustomGenerator {
@Override
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
switch (method.getName()) {
case "isInstance":
generateIsInstance(function, context);
break;
default:
throw new IllegalArgumentException("Unsupported method: " + method);
}
}
private void generateIsInstance(WasmFunction function, WasmGCCustomGeneratorContext context) {
var classCls = context.classInfoProvider().getClassInfo("java.lang.Class");
var objectCls = context.classInfoProvider().getClassInfo("java.lang.Object");
var thisVar = new WasmLocal(classCls.getType());
var objectVar = new WasmLocal(objectCls.getType());
function.add(thisVar);
function.add(objectVar);
var conditional = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(objectVar),
new WasmNullConstant(WasmType.Reference.ANY)));
conditional.setType(WasmType.INT32);
conditional.getThenBlock().getBody().add(new WasmInt32Constant(0));
var objectClass = new WasmStructGet(objectCls.getStructure(), new WasmGetLocal(objectVar),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var functionRef = new WasmStructGet(classCls.getStructure(), new WasmGetLocal(thisVar),
context.classInfoProvider().getClassSupertypeFunctionOffset());
var call = new WasmCallReference(functionRef,
context.functionTypes().of(WasmType.INT32, classCls.getType()));
call.getArguments().add(objectClass);
conditional.getElseBlock().getBody().add(call);
function.getBody().add(new WasmReturn(conditional));
}
}

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.generators.gc;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.model.WasmModule;
@ -25,4 +26,6 @@ public interface WasmGCCustomGeneratorContext {
WasmFunctionTypes functionTypes();
WasmGCTypeMapper typeMapper();
WasmGCClassInfoProvider classInfoProvider();
}

View File

@ -25,10 +25,16 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
private Map<MethodReference, WasmGCCustomGenerator> generators = new HashMap<>();
public WasmGCCustomGenerators() {
fillClass();
fillStringPool();
fillSystem();
}
private void fillClass() {
var classGenerators = new ClassGenerators();
generators.put(new MethodReference(Class.class, "isInstance", Object.class, boolean.class), classGenerators);
}
private void fillStringPool() {
generators.put(
new MethodReference(WasmGCSupport.class, "nextByte", byte.class),

View File

@ -0,0 +1,37 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.intrinsics.gc;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
public class ClassIntrinsics implements WasmGCIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
switch (invocation.getMethod().getName()) {
case "getComponentType":
var cls = context.generate(invocation.getArguments().get(0));
var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure();
var result = new WasmStructGet(clsStruct, cls,
context.classInfoProvider().getClassArrayItemOffset());
result.setLocation(invocation.getLocation());
return result;
default:
throw new IllegalArgumentException("Unsupported invocation method: " + invocation.getMethod());
}
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.intrinsics.gc;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
public class ObjectIntrinsics implements WasmGCIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var obj = context.generate(invocation.getArguments().get(0));
var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure();
var result = new WasmStructGet(objectStruct, obj, WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
result.setLocation(invocation.getLocation());
return result;
}
}

View File

@ -19,6 +19,7 @@ import org.teavm.ast.Expr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.expression.WasmExpression;
@ -38,4 +39,6 @@ public interface WasmGCIntrinsicContext {
ClassHierarchy hierarchy();
WasmGCTypeMapper typeMapper();
WasmGCClassInfoProvider classInfoProvider();
}

View File

@ -24,9 +24,21 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
private Map<MethodReference, WasmGCIntrinsic> intrinsics = new HashMap<>();
public WasmGCIntrinsics() {
fillObject();
fillClass();
fillSystem();
}
private void fillObject() {
var objectIntrinsics = new ObjectIntrinsics();
intrinsics.put(new MethodReference(Object.class, "getClass", Class.class), objectIntrinsics);
}
private void fillClass() {
var classIntrinsics = new ClassIntrinsics();
intrinsics.put(new MethodReference(Class.class, "getComponentType", Class.class), classIntrinsics);
}
private void fillSystem() {
intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class,
int.class, int.class, void.class), new SystemArrayCopyIntrinsic());

View File

@ -0,0 +1,47 @@
/*
* 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 org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ElementModifier;
public class BaseClassesTransformation implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
if (cls.getName().equals("java.lang.Object")) {
for (var method : cls.getMethods()) {
switch (method.getName()) {
case "getClass":
method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE);
break;
}
}
} else if (cls.getName().equals("java.lang.Class")) {
for (var method : cls.getMethods()) {
switch (method.getName()) {
case "getComponentType":
case "isInstance":
method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE);
break;
}
}
}
}
}