diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 397c35d54..3c897b28a 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -73,6 +73,7 @@ import org.teavm.model.instructions.IsInstanceInstruction; import org.teavm.model.instructions.StringConstantInstruction; import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.Characteristics; +import org.teavm.model.util.ReflectionUtil; import org.teavm.runtime.CallSite; import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeClass; @@ -1295,26 +1296,7 @@ public class ClassGenerator { public String nameOfType(ValueType type) { if (type instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) type).getKind()) { - case BOOLEAN: - return "boolean"; - case BYTE: - return "byte"; - case SHORT: - return "short"; - case CHARACTER: - return "char"; - case INTEGER: - return "int"; - case LONG: - return "long"; - case FLOAT: - return "float"; - case DOUBLE: - return "double"; - default: - throw new AssertionError(); - } + return ReflectionUtil.typeName(((ValueType.Primitive) type).getKind()); } else if (type instanceof ValueType.Array) { if (isArrayOfPrimitives(type)) { return type.toString().replace('/', '.'); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index bfdb2c285..62d218840 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -55,6 +55,7 @@ import org.teavm.model.classes.VirtualTable; import org.teavm.model.classes.VirtualTableEntry; import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.lowlevel.Characteristics; +import org.teavm.model.util.ReflectionUtil; import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeObject; @@ -250,34 +251,7 @@ public class WasmClassGenerator { if (type == ValueType.VOID) { name = "void"; } else { - switch (((ValueType.Primitive) type).getKind()) { - case BOOLEAN: - name = "boolean"; - break; - case BYTE: - name = "byte"; - break; - case SHORT: - name = "short"; - break; - case CHARACTER: - name = "char"; - break; - case INTEGER: - name = "int"; - break; - case LONG: - name = "long"; - break; - case FLOAT: - name = "float"; - break; - case DOUBLE: - name = "double"; - break; - default: - name = ""; - } + name = ReflectionUtil.typeName(((ValueType.Primitive) type).getKind()); } value.setAddress(CLASS_NAME, stringPool.getStringPointer(name)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java new file mode 100644 index 000000000..debe36b38 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java @@ -0,0 +1,49 @@ +/* + * 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.generate.gc.classes; + +public final class WasmGCClassFlags { + public static final int ABSTRACT = 1; + public static final int INTERFACE = 1 << 1; + public static final int FINAL = 1 << 2; + public static final int ENUM = 1 << 3; + public static final int ANNOTATION = 1 << 4; + public static final int SYNTHETIC = 1 << 5; + public static final int BRIDGE = 1 << 6; + public static final int DEPRECATED = 1 << 7; + public static final int NATIVE = 1 << 8; + public static final int STATIC = 1 << 9; + public static final int STRICT = 1 << 10; + public static final int SYNCHRONIZED = 1 << 11; + public static final int TRANSIENT = 1 << 12; + public static final int VARARGS = 1 << 13; + public static final int VOLATILE = 1 << 14; + public static final int PRIMITIVE = 1 << 15; + + public static final int PRIMITIVE_KIND_SHIFT = 16; + public static final int PRIMITIVE_BOOLEAN = 0; + public static final int PRIMITIVE_BYTE = 1; + public static final int PRIMITIVE_SHORT = 2; + public static final int PRIMITIVE_CHAR = 3; + public static final int PRIMITIVE_INT = 4; + public static final int PRIMITIVE_LONG = 5; + public static final int PRIMITIVE_FLOAT = 6; + public static final int PRIMITIVE_DOUBLE = 7; + public static final int PRIMITIVE_VOID = 8; + + private WasmGCClassFlags() { + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java new file mode 100644 index 000000000..037b850cf --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -0,0 +1,538 @@ +/* + * 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.generate.gc.classes; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; +import org.teavm.backend.wasm.generate.gc.method.WasmGCFunctionProvider; +import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmGlobal; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmStorageType; +import org.teavm.backend.wasm.model.WasmStructure; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCast; +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.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.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmSetGlobal; +import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; +import org.teavm.backend.wasm.model.expression.WasmStructSet; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.model.analysis.ClassMetadataRequirements; +import org.teavm.model.classes.TagRegistry; +import org.teavm.model.classes.VirtualTable; +import org.teavm.model.classes.VirtualTableProvider; +import org.teavm.model.util.ReflectionUtil; + +public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor { + public static final int CLASS_FIELD_OFFSET = 0; + + private final WasmModule module; + private ClassReaderSource classSource; + private WasmFunctionTypes functionTypes; + private TagRegistry tagRegistry; + private ClassMetadataRequirements metadataRequirements; + private VirtualTableProvider virtualTables; + private WasmGCFunctionProvider functionProvider; + private Map classInfoMap = new LinkedHashMap<>(); + private ObjectIntMap fieldIndexes = new ObjectIntHashMap<>(); + private ObjectIntMap methodIndexes = new ObjectIntHashMap<>(); + + public final WasmGCStringPool strings; + public final WasmGCStandardClasses standardClasses; + private final WasmGCTypeMapper typeMapper; + private final NameProvider names; + private WasmFunction initializer; + private WasmFunction createPrimitiveClassFunction; + private WasmFunction createArrayClassFunction; + private final WasmGCSupertypeFunctionGenerator supertypeGenerator; + + private int classTagOffset; + private int classFlagsOffset; + private int classNameOffset; + private int classParentOffset; + private int classArrayOffset; + private int classArrayItemOffset; + private int classSupertypeFunctionOffset; + private int virtualTableFieldOffset; + + public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, + WasmFunctionTypes functionTypes, TagRegistry tagRegistry, + ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables, + WasmGCFunctionProvider functionProvider, NameProvider names) { + this.module = module; + this.classSource = classSource; + this.functionTypes = functionTypes; + this.tagRegistry = tagRegistry; + this.metadataRequirements = metadataRequirements; + this.virtualTables = virtualTables; + this.functionProvider = functionProvider; + this.names = names; + standardClasses = new WasmGCStandardClasses(this); + strings = new WasmGCStringPool(standardClasses, module, functionProvider); + supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); + typeMapper = new WasmGCTypeMapper(this); + } + + @Override + public void contributeToInitializerDefinitions(WasmFunction function) { + for (var classInfo : classInfoMap.values()) { + var newStruct = new WasmStructNewDefault(standardClasses.classClass().getStructure()); + function.getBody().add(new WasmSetGlobal(classInfo.pointer, newStruct)); + } + } + + @Override + public void contributeToInitializer(WasmFunction function) { + var classClass = standardClasses.classClass(); + for (var classInfo : classInfoMap.values()) { + classInfo.initializer.accept(function.getBody()); + var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); + function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, + new WasmFunctionReference(supertypeFunction))); + function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, + new WasmGetGlobal(classClass.pointer))); + } + } + + @Override + public WasmGCClassInfo getClassInfo(ValueType type) { + var classInfo = classInfoMap.get(type); + if (classInfo == null) { + classInfo = new WasmGCClassInfo(type); + classInfoMap.put(type, classInfo); + if (!(type instanceof ValueType.Primitive)) { + var name = type instanceof ValueType.Object + ? ((ValueType.Object) type).getClassName() + : null; + classInfo.structure = new WasmStructure(name); + classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage()); + fillFields(classInfo.structure.getFields(), type); + } + var pointerName = names.forClassInstance(type); + var classStructure = type instanceof ValueType.Object + ? initRegularClassStructure(((ValueType.Object) type).getClassName()) + : standardClasses.classClass().getStructure(); + classInfo.virtualTableStructure = classStructure; + classInfo.pointer = new WasmGlobal(pointerName, classStructure.getReference(), + new WasmNullConstant(classStructure.getReference())); + module.globals.add(classInfo.pointer); + if (type instanceof ValueType.Primitive) { + initPrimitiveClass(classInfo, (ValueType.Primitive) type); + } else if (type instanceof ValueType.Void) { + initVoidClass(classInfo); + } else if (type instanceof ValueType.Array) { + initArrayClass(classInfo, (ValueType.Array) type); + } else if (type instanceof ValueType.Object) { + initRegularClass(classInfo, classStructure, ((ValueType.Object) type).getClassName()); + } + } + return classInfo; + } + + public int getClassTagOffset() { + return classTagOffset; + } + + public int getClassArrayItemOffset() { + return classArrayItemOffset; + } + + private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) { + classInfo.initializer = target -> { + int kind; + switch (type.getKind()) { + case BOOLEAN: + kind = WasmGCClassFlags.PRIMITIVE_BOOLEAN; + break; + case BYTE: + kind = WasmGCClassFlags.PRIMITIVE_BYTE; + break; + case SHORT: + kind = WasmGCClassFlags.PRIMITIVE_SHORT; + break; + case CHARACTER: + kind = WasmGCClassFlags.PRIMITIVE_CHAR; + break; + case INTEGER: + kind = WasmGCClassFlags.PRIMITIVE_INT; + break; + case LONG: + kind = WasmGCClassFlags.PRIMITIVE_LONG; + break; + case FLOAT: + kind = WasmGCClassFlags.PRIMITIVE_FLOAT; + break; + case DOUBLE: + kind = WasmGCClassFlags.PRIMITIVE_DOUBLE; + break; + default: + throw new IllegalArgumentException(); + } + target.add(fillPrimitiveClass( + classInfo.pointer, + ReflectionUtil.typeName(type.getKind()), + kind + )); + }; + } + + private void initVoidClass(WasmGCClassInfo classInfo) { + classInfo.initializer = target -> { + target.add(fillPrimitiveClass( + classInfo.pointer, + "void", + WasmGCClassFlags.PRIMITIVE_VOID + )); + }; + } + + private void initRegularClass(WasmGCClassInfo classInfo, WasmStructure classStructure, String name) { + classInfo.initializer = target -> { + var ranges = tagRegistry.getRanges(name); + int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0); + target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag))); + var metadataReg = metadataRequirements.getInfo(name); + if (metadataReg.name()) { + var namePtr = strings.getStringConstant(name).global; + target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); + } + var cls = classSource.get(name); + if (cls != null) { + if (metadataReg.simpleName() && cls.getSimpleName() != null) { + var namePtr = strings.getStringConstant(cls.getSimpleName()).global; + target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); + } + if (cls.getParent() != null) { + var parent = getClassInfo(cls.getParent()); + target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer))); + } + } + var virtualTable = virtualTables.lookup(name); + fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTableFieldOffset, + name); + }; + } + + private int fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, + VirtualTable virtualTable, int index, String origin) { + if (virtualTable.getParent() != null) { + index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin); + } + for (var method : virtualTable.getMethods()) { + var entry = virtualTable.getEntry(method); + if (entry != null && entry.getImplementor() != null) { + var function = functionProvider.getMemberFunction(entry.getImplementor()); + if (!origin.equals(entry.getImplementor().getClassName())) { + var functionType = getFunctionType(virtualTable.getClassName(), method); + var wrapperFunction = new WasmFunction(functionType); + module.functions.add(wrapperFunction); + var call = new WasmCall(function); + var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType()); + wrapperFunction.getLocalVariables().add(instanceParam); + var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType(); + call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget)); + var params = new WasmLocal[method.parameterCount()]; + for (var i = 0; i < method.parameterCount(); ++i) { + params[i] = new WasmLocal(typeMapper.mapType(method.parameterType(i)).asUnpackedType()); + call.getArguments().add(new WasmGetLocal(params[i])); + } + wrapperFunction.getLocalVariables().addAll(List.of(params)); + } + var ref = new WasmFunctionReference(function); + target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref)); + } + } + return index; + } + + private WasmStructure initRegularClassStructure(String className) { + var virtualTable = virtualTables.lookup(className); + var structure = new WasmStructure(names.forClassClass(className)); + module.types.add(structure); + addVirtualTableFields(structure, virtualTable); + fillClassFields(structure.getFields(), "java.lang.Class"); + return structure; + } + + private void addVirtualTableFields(WasmStructure structure, VirtualTable virtualTable) { + if (virtualTable.getParent() != null) { + addVirtualTableFields(structure, virtualTable.getParent()); + } + for (var methodDesc : virtualTable.getMethods()) { + var functionType = getFunctionType(virtualTable.getClassName(), methodDesc); + var methodRef = new MethodReference(virtualTable.getClassName(), methodDesc); + methodIndexes.put(methodRef, structure.getFields().size()); + structure.getFields().add(functionType.getReference().asStorage()); + } + } + + private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) { + var returnType = typeMapper.mapType(methodDesc.getResultType()).asUnpackedType(); + var javaParamTypes = methodDesc.getParameterTypes(); + var paramTypes = new WasmType[javaParamTypes.length + 1]; + paramTypes[0] = getClassInfo(className).getType(); + for (var i = 0; i < javaParamTypes.length; ++i) { + paramTypes[i + 1] = typeMapper.mapType(javaParamTypes[i]).asUnpackedType(); + } + return functionTypes.of(returnType, paramTypes); + } + + private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) { + classInfo.initializer = target -> { + var itemTypeInfo = getClassInfo(type.getItemType()); + target.add(new WasmCall( + getCreateArrayClassFunction(), + new WasmGetGlobal(classInfo.pointer), + new WasmGetGlobal(itemTypeInfo.pointer) + )); + }; + } + + private WasmExpression fillPrimitiveClass(WasmGlobal global, String name, int kind) { + return new WasmCall( + getCreatePrimitiveClassFunction(), + new WasmGetGlobal(global), + new WasmGetGlobal(strings.getStringConstant(name).global), + new WasmInt32Constant(kind) + ); + } + + public int getFieldIndex(FieldReference fieldRef) { + var result = fieldIndexes.getOrDefault(fieldRef, -1); + if (result < 0) { + throw new IllegalStateException("Can't get offset of field " + fieldRef); + } + return result; + } + + public int getVirtualMethodIndex(MethodReference methodRef) { + var result = methodIndexes.getOrDefault(methodRef, -1); + if (result < 0) { + throw new IllegalStateException("Can't get offset of method " + methodRef); + } + return result; + } + + private void fillFields(List fields, ValueType type) { + fields.add(standardClasses.classClass().getType().asStorage()); + if (type instanceof ValueType.Object) { + fillClassFields(fields, ((ValueType.Object) type).getClassName()); + } else if (type instanceof ValueType.Array) { + fillArrayField(fields, ((ValueType.Array) type).getItemType()); + } + } + + private void fillClassFields(List fields, String className) { + var classReader = classSource.get(className); + if (classReader.hasModifier(ElementModifier.INTERFACE)) { + fillSimpleClassFields(fields, "java.lang.Object"); + } else { + fillSimpleClassFields(fields, className); + } + } + + private void fillSimpleClassFields(List fields, String className) { + var classReader = classSource.get(className); + if (classReader.getParent() != null) { + fillClassFields(fields, classReader.getParent()); + } else { + fields.add(standardClasses.classClass().getType().asStorage()); + } + for (var field : classReader.getFields()) { + if (field.hasModifier(ElementModifier.STATIC)) { + continue; + } + fieldIndexes.putIfAbsent(field.getReference(), fields.size()); + fields.add(typeMapper.mapType(field.getType())); + } + if (className.equals("java.lang.Class")) { + classFlagsOffset = fields.size(); + fields.add(WasmType.INT32.asStorage()); + classTagOffset = fields.size(); + fields.add(WasmType.INT32.asStorage()); + classParentOffset = fields.size(); + fields.add(standardClasses.classClass().getType().asStorage()); + classArrayItemOffset = fields.size(); + fields.add(standardClasses.classClass().getType().asStorage()); + classArrayOffset = fields.size(); + fields.add(standardClasses.classClass().getType().asStorage()); + classSupertypeFunctionOffset = fields.size(); + fields.add(supertypeGenerator.getFunctionType().getReference().asStorage()); + virtualTableFieldOffset = fields.size(); + classNameOffset = fieldIndexes.get(new FieldReference(className, "name")); + } + } + + private void fillArrayField(List fields, ValueType elementType) { + var wasmArray = new WasmArray(null, () -> typeMapper.mapType(elementType)); + module.types.add(wasmArray); + fields.add(wasmArray.getReference().asStorage()); + } + + private WasmFunction getCreatePrimitiveClassFunction() { + if (createPrimitiveClassFunction == null) { + createPrimitiveClassFunction = createCreatePrimitiveClassFunction(); + } + return createPrimitiveClassFunction; + } + + private WasmFunction createCreatePrimitiveClassFunction() { + var functionType = functionTypes.of( + null, + standardClasses.classClass().getType(), + standardClasses.stringClass().getType(), + WasmType.INT32 + ); + var function = new WasmFunction(functionType); + function.setName("_teavm_fill_primitive_class_"); + + var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); + var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name"); + var kindVar = new WasmLocal(WasmType.INT32, "kind"); + function.getLocalVariables().add(targetVar); + function.getLocalVariables().add(nameVar); + function.getLocalVariables().add(kindVar); + + var flagsExpr = new WasmIntBinary( + WasmIntType.INT32, + WasmIntBinaryOperation.SHL, + new WasmGetLocal(kindVar), + new WasmInt32Constant(WasmGCClassFlags.PRIMITIVE_KIND_SHIFT) + ); + flagsExpr = new WasmIntBinary( + WasmIntType.INT32, + WasmIntBinaryOperation.OR, + flagsExpr, + new WasmInt32Constant(WasmGCClassFlags.FINAL | WasmGCClassFlags.PRIMITIVE) + ); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classFlagsOffset, + flagsExpr + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classNameOffset, + new WasmGetLocal(nameVar) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classTagOffset, + new WasmInt32Constant(Integer.MAX_VALUE) + )); + return function; + } + + private WasmFunction getCreateArrayClassFunction() { + if (createArrayClassFunction == null) { + createArrayClassFunction = createCreateArrayClassFunction(); + } + return createCreateArrayClassFunction(); + } + + private WasmFunction createCreateArrayClassFunction() { + var functionType = functionTypes.of( + null, + standardClasses.classClass().getType(), + standardClasses.classClass().getType() + ); + var function = new WasmFunction(functionType); + function.setName("_teavm_fill_array_class_"); + + var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); + var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item"); + function.getLocalVariables().add(targetVar); + function.getLocalVariables().add(itemVar); + + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classFlagsOffset, + new WasmInt32Constant(WasmGCClassFlags.FINAL) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classArrayItemOffset, + new WasmGetLocal(itemVar) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(itemVar), + classArrayOffset, + new WasmGetLocal(targetVar) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classTagOffset, + new WasmInt32Constant(0) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classParentOffset, + new WasmGetGlobal(standardClasses.classClass().pointer) + )); + return function; + } + + + private WasmFunction getInitializer() { + if (initializer == null) { + initializer = new WasmFunction(functionTypes.of(null)); + initializer.setName("_teavm_init_classes_"); + module.functions.add(initializer); + } + return initializer; + } + + private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) { + return new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetGlobal(classInfo.pointer), + fieldIndex, + value + ); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java new file mode 100644 index 000000000..7b1d28c8c --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java @@ -0,0 +1,56 @@ +/* + * 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.generate.gc.classes; + +import java.util.List; +import java.util.function.Consumer; +import org.teavm.backend.wasm.model.WasmGlobal; +import org.teavm.backend.wasm.model.WasmStructure; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.model.ValueType; + +public class WasmGCClassInfo { + private ValueType valueType; + WasmStructure structure; + WasmStructure virtualTableStructure; + WasmGlobal pointer; + Consumer> initializer; + + WasmGCClassInfo(ValueType valueType) { + this.valueType = valueType; + } + + public ValueType getValueType() { + return valueType; + } + + public WasmStructure getStructure() { + return structure; + } + + public WasmStructure getVirtualTableStructure() { + return virtualTableStructure; + } + + public WasmType.CompositeReference getType() { + return getStructure().getReference(); + } + + public WasmGlobal getPointer() { + return pointer; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java new file mode 100644 index 000000000..b29f80015 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java @@ -0,0 +1,26 @@ +/* + * 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.generate.gc.classes; + +import org.teavm.model.ValueType; + +public interface WasmGCClassInfoProvider { + WasmGCClassInfo getClassInfo(ValueType type); + + default WasmGCClassInfo getClassInfo(String name) { + return getClassInfo(ValueType.object(name)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCStandardClasses.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCStandardClasses.java new file mode 100644 index 000000000..73c858c75 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCStandardClasses.java @@ -0,0 +1,48 @@ +/* + * 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.generate.gc.classes; + +public class WasmGCStandardClasses { + private WasmGCClassInfoProvider classGenerator; + private WasmGCClassInfo classClassInfo; + private WasmGCClassInfo stringClassInfo; + private WasmGCClassInfo objectClassInfo; + + public WasmGCStandardClasses(WasmGCClassInfoProvider classGenerator) { + this.classGenerator = classGenerator; + } + + public WasmGCClassInfo classClass() { + if (classClassInfo == null) { + classClassInfo = classGenerator.getClassInfo("java.lang.Class"); + } + return classClassInfo; + } + + public WasmGCClassInfo stringClass() { + if (stringClassInfo == null) { + stringClassInfo = classGenerator.getClassInfo("java.lang.Class"); + } + return stringClassInfo; + } + + public WasmGCClassInfo objectClass() { + if (objectClassInfo == null) { + objectClassInfo = classGenerator.getClassInfo("java.lang.Object"); + } + return objectClassInfo; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java new file mode 100644 index 000000000..5a6794467 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java @@ -0,0 +1,186 @@ +/* + * 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.generate.gc.classes; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmLocal; +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.WasmConditional; +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.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.model.ValueType; +import org.teavm.model.classes.TagRegistry; + +public class WasmGCSupertypeFunctionGenerator { + private ObjectIntMap tableIndexes = new ObjectIntHashMap<>(); + private Map functions = new HashMap<>(); + private WasmModule module; + private WasmGCClassGenerator classGenerator; + private NameProvider nameProvider; + private TagRegistry tagRegistry; + private WasmFunctionTypes functionTypes; + private WasmFunctionType functionType; + + WasmGCSupertypeFunctionGenerator( + WasmModule module, + WasmGCClassGenerator classGenerator, + NameProvider nameProvider, + TagRegistry tagRegistry, + WasmFunctionTypes functionTypes + ) { + this.module = module; + this.classGenerator = classGenerator; + this.nameProvider = nameProvider; + this.tagRegistry = tagRegistry; + this.functionTypes = functionTypes; + } + + public WasmFunction getIsSupertypeFunction(ValueType type) { + var result = functions.get(type); + if (result == null) { + result = generateIsSupertypeFunction(type); + functions.put(type, result); + } + return result; + } + + private WasmFunction generateIsSupertypeFunction(ValueType type) { + var function = new WasmFunction(getFunctionType()); + function.setName(nameProvider.forSupertypeFunction(type)); + var subtypeVar = new WasmLocal(WasmType.INT32, "subtype"); + function.add(subtypeVar); + module.functions.add(function); + + if (type instanceof ValueType.Object) { + var className = ((ValueType.Object) type).getClassName(); + generateIsClass(subtypeVar, className, function.getBody()); + } else if (type instanceof ValueType.Array) { + ValueType itemType = ((ValueType.Array) type).getItemType(); + generateIsArray(subtypeVar, itemType, function.getBody()); + } else { + var expected = classGenerator.getClassInfo(type).pointer; + var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, + new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected)); + function.getBody().add(new WasmReturn(condition)); + } + + return function; + } + + private void generateIsClass(WasmLocal subtypeVar, String className, List body) { + var ranges = tagRegistry.getRanges(className); + if (ranges.isEmpty()) { + body.add(new WasmReturn(new WasmInt32Constant(0))); + return; + } + + int tagOffset = classGenerator.getClassTagOffset(); + + var tagExpression = getClassField(new WasmGetLocal(subtypeVar), tagOffset); + body.add(new WasmSetLocal(subtypeVar, tagExpression)); + + ranges.sort(Comparator.comparingInt(range -> range.lower)); + + int lower = ranges.get(0).lower; + int upper = ranges.get(ranges.size() - 1).upper; + + var lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, + new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower)); + var testLower = new WasmConditional(lowerCondition); + testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); + body.add(testLower); + + var upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, + new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper)); + var testUpper = new WasmConditional(upperCondition); + testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); + body.add(testUpper); + + for (int i = 1; i < ranges.size(); ++i) { + int lowerHole = ranges.get(i - 1).upper; + int upperHole = ranges.get(i).lower; + + lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, + new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole)); + testLower = new WasmConditional(lowerCondition); + body.add(testLower); + + upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, + new WasmGetLocal(subtypeVar), new WasmInt32Constant(upperHole)); + testUpper = new WasmConditional(upperCondition); + testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); + + testLower.getThenBlock().getBody().add(testUpper); + } + + body.add(new WasmReturn(new WasmInt32Constant(1))); + } + + private void generateIsArray(WasmLocal subtypeVar, ValueType itemType, List body) { + int itemOffset = classGenerator.getClassArrayItemOffset(); + + var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset); + body.add(new WasmSetLocal(subtypeVar, itemExpression)); + + var itemTest = new WasmConditional(new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, + new WasmGetLocal(subtypeVar))); + itemTest.setType(WasmType.INT32); + itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); + + var delegateToItem = new WasmCall(getIsSupertypeFunction(itemType)); + delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar)); + itemTest.getElseBlock().getBody().add(delegateToItem); + + body.add(new WasmReturn(itemTest)); + } + + public WasmFunctionType getFunctionType() { + if (functionType == null) { + functionType = functionTypes.of(WasmType.INT32, classGenerator.standardClasses.classClass().getType()); + } + return functionType; + } + + + private WasmExpression getClassField(WasmExpression instance, int fieldIndex) { + return new WasmStructGet( + classGenerator.standardClasses.classClass().getStructure(), + instance, + fieldIndex + ); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java new file mode 100644 index 000000000..b44e537c9 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java @@ -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.backend.wasm.generate.gc.classes; + +import org.teavm.backend.wasm.model.WasmPackedType; +import org.teavm.backend.wasm.model.WasmStorageType; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.model.ValueType; + +public class WasmGCTypeMapper { + private WasmGCClassInfoProvider classInfoProvider; + + WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider) { + this.classInfoProvider = classInfoProvider; + } + + public WasmStorageType mapType(ValueType type) { + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BYTE: + case BOOLEAN: + return WasmStorageType.packed(WasmPackedType.INT8); + case SHORT: + case CHARACTER: + return WasmStorageType.packed(WasmPackedType.INT8); + case INTEGER: + return WasmType.INT32.asStorage(); + case LONG: + return WasmType.INT64.asStorage(); + case FLOAT: + return WasmType.FLOAT32.asStorage(); + case DOUBLE: + return WasmType.FLOAT64.asStorage(); + default: + throw new IllegalArgumentException(); + } + } else { + return classInfoProvider.getClassInfo(type).getType().asStorage(); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/initialization/WasmGCInitializerContributor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/initialization/WasmGCInitializerContributor.java new file mode 100644 index 000000000..9cb4a0b46 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/initialization/WasmGCInitializerContributor.java @@ -0,0 +1,24 @@ +/* + * 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.generate.gc.initialization; + +import org.teavm.backend.wasm.model.WasmFunction; + +public interface WasmGCInitializerContributor { + void contributeToInitializerDefinitions(WasmFunction function); + + void contributeToInitializer(WasmFunction function); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/method/WasmGCFunctionProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/method/WasmGCFunctionProvider.java new file mode 100644 index 000000000..195501fb8 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/method/WasmGCFunctionProvider.java @@ -0,0 +1,25 @@ +/* + * 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.generate.gc.method; + +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.model.MethodReference; + +public interface WasmGCFunctionProvider { + WasmFunction getMemberFunction(MethodReference methodRef); + + WasmFunction getStaticFunction(MethodReference methodRef); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringConstant.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringConstant.java new file mode 100644 index 000000000..2c04a1a6c --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringConstant.java @@ -0,0 +1,28 @@ +/* + * 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.generate.gc.strings; + +import org.teavm.backend.wasm.model.WasmGlobal; + +public class WasmGCStringConstant { + public final int index; + public final WasmGlobal global; + + public WasmGCStringConstant(int index, WasmGlobal global) { + this.index = index; + this.global = global; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java new file mode 100644 index 000000000..68db5e618 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java @@ -0,0 +1,120 @@ +/* + * 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.generate.gc.strings; + +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; +import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; +import org.teavm.backend.wasm.generate.gc.method.WasmGCFunctionProvider; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmGlobal; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmSetGlobal; +import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; +import org.teavm.backend.wasm.render.WasmBinaryWriter; +import org.teavm.model.MethodReference; + +public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor { + private WasmGCStandardClasses standardClasses; + private WasmModule module; + private WasmBinaryWriter binaryWriter = new WasmBinaryWriter(); + private Map stringMap = new LinkedHashMap<>(); + private WasmGCFunctionProvider functionProvider; + + public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module, + WasmGCFunctionProvider functionProvider) { + this.standardClasses = standardClasses; + this.module = module; + this.functionProvider = functionProvider; + } + + @Override + public void contributeToInitializerDefinitions(WasmFunction function) { + for (var str : stringMap.values()) { + var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure()); + function.getBody().add(new WasmSetGlobal(str.global, newStruct)); + } + } + + @Override + public void contributeToInitializer(WasmFunction function) { + var nextCharArrayFunction = functionProvider.getStaticFunction(new MethodReference(WasmGCStringPool.class, + "nextCharArray", char[].class)); + } + + @Override + public WasmGCStringConstant getStringConstant(String string) { + return stringMap.computeIfAbsent(string, s -> { + binaryWriter.writeInt32(string.length()); + binaryWriter.writeBytes(string.getBytes(StandardCharsets.UTF_8)); + var globalName = "_teavm_java_string_" + stringMap.size(); + var globalType = standardClasses.stringClass().getType(); + var global = new WasmGlobal(globalName, globalType, WasmExpression.defaultValueOfType(globalType)); + module.globals.add(global); + return new WasmGCStringConstant(stringMap.size(), global); + }); + } + + static char[] nextCharArray() { + var length = nextLEB(); + var result = new char[length]; + var pos = 0; + while (pos < length) { + var b = nextByte(); + if ((b & 0x80) == 0) { + result[pos++] = (char) b; + } else if ((b & 0xE0) == 0xC0) { + var b2 = nextByte(); + result[pos++] = (char) (((b & 0x1F) << 6) | (b2 & 0x3F)); + } else if ((b & 0xF0) == 0xE0) { + var b2 = nextByte(); + var b3 = nextByte(); + var c = (char) (((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F)); + result[pos++] = c; + } else if ((b & 0xF8) == 0xF0) { + var b2 = nextByte(); + var b3 = nextByte(); + var b4 = nextByte(); + var code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F); + result[pos++] = Character.highSurrogate(code); + result[pos++] = Character.lowSurrogate(code); + } + } + return result; + } + + private static int nextLEB() { + var shift = 0; + var result = 0; + while (true) { + var b = nextByte(); + var digit = b & 0x7F; + result |= digit << shift; + if ((b & 0x80) == 0) { + break; + } + shift += 7; + } + return result; + } + + private static native byte nextByte(); + + private static native void error(); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringProvider.java new file mode 100644 index 000000000..4e36c587d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringProvider.java @@ -0,0 +1,20 @@ +/* + * 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.generate.gc.strings; + +public interface WasmGCStringProvider { + WasmGCStringConstant getStringConstant(String string); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java index e0da99d22..f71b561b3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java @@ -17,7 +17,7 @@ package org.teavm.backend.wasm.model; public abstract class WasmCompositeType extends WasmEntity { private String name; - private WasmType.Reference reference; + private WasmType.CompositeReference reference; WasmCompositeType(String name) { this.name = name; @@ -27,9 +27,9 @@ public abstract class WasmCompositeType extends WasmEntity { return name; } - public WasmType.Reference getReference() { + public WasmType.CompositeReference getReference() { if (reference == null) { - reference = new WasmType.Reference(this); + reference = new WasmType.CompositeReference(this); } return reference; } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java index 282c0ad8e..142cbe8a0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java @@ -16,27 +16,15 @@ package org.teavm.backend.wasm.model; import java.util.List; -import java.util.function.Supplier; public class WasmStructure extends WasmCompositeType { - private List fields; - private Supplier> fieldsSupplier; + private List fields; - public WasmStructure(String name, List fields) { + public WasmStructure(String name) { super(name); - this.fields = List.copyOf(fields); } - public WasmStructure(String name, Supplier> fieldsSupplier) { - super(name); - this.fieldsSupplier = fieldsSupplier; - } - - public List getFields() { - if (fields == null) { - fields = List.copyOf(fieldsSupplier.get()); - fieldsSupplier = null; - } + public List getFields() { return fields; } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java index e3c3a31e2..b47fb0d1c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java @@ -56,11 +56,44 @@ public abstract class WasmType { } } - public static final class Reference extends WasmType { + public static abstract class Reference extends WasmType { + public static final SpecialReference FUNC = SpecialReferenceKind.FUNC.asType(); + public static final SpecialReference ANY = SpecialReferenceKind.ANY.asType(); + public static final SpecialReference EXTERN = SpecialReferenceKind.EXTERN.asType(); + public static final SpecialReference STRUCT = SpecialReferenceKind.STRUCT.asType(); + public static final SpecialReference ARRAY = SpecialReferenceKind.ARRAY.asType(); + } + + public static final class CompositeReference extends Reference { public final WasmCompositeType composite; - Reference(WasmCompositeType composite) { + CompositeReference(WasmCompositeType composite) { this.composite = composite; } } + + public static final class SpecialReference extends WasmType { + public final SpecialReferenceKind kind; + + private SpecialReference(SpecialReferenceKind kind) { + this.kind = kind; + } + } + + public enum SpecialReferenceKind { + FUNC, + ANY, + EXTERN, + STRUCT, + ARRAY; + + private SpecialReference type; + + final SpecialReference asType() { + if (type == null) { + type = new SpecialReference(this); + } + return type; + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCallReference.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCallReference.java new file mode 100644 index 000000000..2a1473585 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCallReference.java @@ -0,0 +1,57 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.teavm.backend.wasm.model.WasmFunctionType; + +public class WasmCallReference extends WasmExpression { + private WasmFunctionType type; + private WasmExpression functionReference; + private List arguments = new ArrayList<>(); + + public WasmCallReference(WasmExpression functionReference, WasmFunctionType type) { + this.functionReference = Objects.requireNonNull(functionReference); + this.type = Objects.requireNonNull(type); + } + + public WasmExpression getFunctionReference() { + return functionReference; + } + + public void setFunctionReference(WasmExpression functionReference) { + this.functionReference = Objects.requireNonNull(functionReference); + } + + public List getArguments() { + return arguments; + } + + public WasmFunctionType getType() { + return type; + } + + public void setType(WasmFunctionType type) { + this.type = Objects.requireNonNull(type); + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java index 02cd7bf2c..4585d2307 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java @@ -145,6 +145,14 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { } } + @Override + public void visit(WasmCallReference expression) { + expression.getFunctionReference().acceptVisitor(this); + for (var argument : expression.getArguments()) { + argument.acceptVisitor(this); + } + } + @Override public void visit(WasmDrop expression) { expression.getOperand().acceptVisitor(this); @@ -250,6 +258,10 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { } } + @Override + public void visit(WasmStructNewDefault expression) { + } + @Override public void visit(WasmStructGet expression) { expression.getInstance().acceptVisitor(this); @@ -283,4 +295,8 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { public void visit(WasmArrayLength expression) { expression.getInstance().acceptVisitor(this); } + + @Override + public void visit(WasmFunctionReference expression) { + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java index 2f3d88839..9d422c36e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java @@ -56,7 +56,7 @@ public abstract class WasmExpression { throw new IllegalArgumentException(); } } else if (type instanceof WasmType.Reference) { - return new WasmNullConstant(((WasmType.Reference) type).composite); + return new WasmNullConstant((WasmType.Reference) type); } else { throw new IllegalArgumentException(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java index 0c65c0bc0..cc6ec3089 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java @@ -62,6 +62,8 @@ public interface WasmExpressionVisitor { void visit(WasmIndirectCall expression); + void visit(WasmCallReference expression); + void visit(WasmDrop expression); void visit(WasmLoadInt32 expression); @@ -96,6 +98,8 @@ public interface WasmExpressionVisitor { void visit(WasmStructNew expression); + void visit(WasmStructNewDefault expression); + void visit(WasmStructGet expression); void visit(WasmStructSet expression); @@ -107,4 +111,6 @@ public interface WasmExpressionVisitor { void visit(WasmArraySet expression); void visit(WasmArrayLength expression); + + void visit(WasmFunctionReference expression); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmFunctionReference.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmFunctionReference.java new file mode 100644 index 000000000..6ba98c2a0 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmFunctionReference.java @@ -0,0 +1,40 @@ +/* + * 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; + +import java.util.Objects; +import org.teavm.backend.wasm.model.WasmFunction; + +public class WasmFunctionReference extends WasmExpression { + private WasmFunction function; + + public WasmFunctionReference(WasmFunction function) { + this.function = Objects.requireNonNull(function); + } + + public WasmFunction getFunction() { + return function; + } + + public void setFunction(WasmFunction function) { + this.function = Objects.requireNonNull(function); + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java index a84c2fb0a..0dceb7c49 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java @@ -15,20 +15,20 @@ */ package org.teavm.backend.wasm.model.expression; -import org.teavm.backend.wasm.model.WasmCompositeType; +import org.teavm.backend.wasm.model.WasmType; public class WasmNullConstant extends WasmExpression { - public WasmCompositeType type; + public WasmType.Reference type; - public WasmNullConstant(WasmCompositeType type) { + public WasmNullConstant(WasmType.Reference type) { this.type = type; } - public WasmCompositeType getType() { + public WasmType.Reference getType() { return type; } - public void setType(WasmCompositeType type) { + public void setType(WasmType.Reference type) { this.type = type; } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java index 3ea740dbd..23357ffab 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java @@ -173,6 +173,13 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { replaceExpressions(expression.getArguments()); } + @Override + public void visit(WasmCallReference expression) { + expression.getFunctionReference().acceptVisitor(this); + expression.setFunctionReference(mapper.apply(expression.getFunctionReference())); + replaceExpressions(expression.getArguments()); + } + @Override public void visit(WasmDrop expression) { expression.getOperand().acceptVisitor(this); @@ -295,6 +302,11 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { @Override public void visit(WasmStructNew expression) { + replaceExpressions(expression.getInitializers()); + } + + @Override + public void visit(WasmStructNewDefault expression) { } @Override @@ -344,4 +356,8 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { expression.getInstance().acceptVisitor(this); expression.setInstance(mapper.apply(expression.getInstance())); } + + @Override + public void visit(WasmFunctionReference expression) { + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructNewDefault.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructNewDefault.java new file mode 100644 index 000000000..a45ca09b6 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructNewDefault.java @@ -0,0 +1,43 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.teavm.backend.wasm.model.WasmStructure; + +public class WasmStructNewDefault extends WasmExpression { + private WasmStructure type; + private List initializers = new ArrayList<>(); + + public WasmStructNewDefault(WasmStructure type) { + this.type = Objects.requireNonNull(type); + } + + public WasmStructure getType() { + return type; + } + + public void setType(WasmStructure type) { + this.type = Objects.requireNonNull(type); + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java index 12eba25bf..c5a045580 100644 --- a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java +++ b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java @@ -25,6 +25,7 @@ import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; +import org.teavm.backend.wasm.model.expression.WasmFunctionReference; public class UnusedFunctionElimination { private WasmModule module; @@ -64,10 +65,13 @@ public class UnusedFunctionElimination { @Override public void visit(WasmCall expression) { super.visit(expression); - var function = expression.getFunction(); - if (function != null) { - use(function); - } + use(expression.getFunction()); + } + + @Override + public void visit(WasmFunctionReference expression) { + super.visit(expression); + use(expression.getFunction()); } }; } diff --git a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java index 66b7d5ecd..3c58ca51d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java +++ b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java @@ -27,9 +27,19 @@ import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmStorageType; import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmArrayGet; +import org.teavm.backend.wasm.model.expression.WasmArraySet; +import org.teavm.backend.wasm.model.expression.WasmCallReference; +import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; +import org.teavm.backend.wasm.model.expression.WasmFunctionReference; import org.teavm.backend.wasm.model.expression.WasmIndirectCall; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.backend.wasm.model.expression.WasmStructNew; +import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; +import org.teavm.backend.wasm.model.expression.WasmStructSet; public class UnusedTypeElimination { private WasmModule module; @@ -51,15 +61,27 @@ public class UnusedTypeElimination { for (var tag : module.tags) { use(tag.getType()); } + for (var global : module.globals) { + use(global.getType()); + } } private void useFrom(WasmFunction function) { use(function.getType()); + for (var local : function.getLocalVariables()) { + use(local.getType()); + } for (var part : function.getBody()) { part.acceptVisitor(exprVisitor); } } + private void use(WasmType type) { + if (type instanceof WasmType.CompositeReference) { + use(((WasmType.CompositeReference) type).composite); + } + } + private void use(WasmCompositeType type) { if (!usedTypes.add(type)) { return; @@ -73,6 +95,67 @@ public class UnusedTypeElimination { super.visit(expression); use(expression.getType()); } + + @Override + public void visit(WasmCast expression) { + super.visit(expression); + use(expression.getTargetType()); + } + + + @Override + public void visit(WasmArrayGet expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmArraySet expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmStructNew expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmStructNewDefault expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmStructGet expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmStructSet expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmNullConstant expression) { + super.visit(expression); + use(expression.getType()); + } + + @Override + public void visit(WasmFunctionReference expression) { + super.visit(expression); + useFrom(expression.getFunction()); + } + + @Override + public void visit(WasmCallReference expression) { + super.visit(expression); + use(expression.getType()); + } }; private WasmCompositeTypeVisitor typeVisitor = new WasmDefaultCompositeTypeVisitor() { @@ -105,8 +188,8 @@ public class UnusedTypeElimination { } private void visit(WasmType type) { - if (type instanceof WasmType.Reference) { - use(((WasmType.Reference) type).composite); + if (type instanceof WasmType.CompositeReference) { + use(((WasmType.CompositeReference) type).composite); } } }; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index f6fa39d8c..8b3269ab5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -33,6 +33,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; @@ -45,6 +46,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatUnary; +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.WasmIndirectCall; @@ -68,6 +70,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStructGet; 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.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmThrow; @@ -250,7 +253,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmNullConstant expression) { pushLocation(expression); writer.writeByte(0xD0); - writer.writeSignedLEB(module.types.indexOf(expression.getType())); + writeBlockType(expression.getType()); popLocation(); } @@ -776,6 +779,18 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmCallReference expression) { + pushLocation(expression); + for (var argument : expression.getArguments()) { + argument.acceptVisitor(this); + } + expression.getFunctionReference().acceptVisitor(this); + writer.writeByte(0x14); + writer.writeLEB(module.types.indexOf(expression.getType())); + popLocation(); + } + @Override public void visit(WasmDrop expression) { pushLocation(expression); @@ -1031,6 +1046,15 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmStructNewDefault expression) { + pushLocation(expression); + writer.writeByte(0xfb); + writer.writeByte(1); + writer.writeInt32(module.types.indexOf(expression.getType())); + popLocation(); + } + @Override public void visit(WasmStructGet expression) { pushLocation(expression); @@ -1118,6 +1142,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmFunctionReference expression) { + pushLocation(expression); + writer.writeByte(0xd2); + writer.writeInt32(module.functions.indexOf(expression.getFunction())); + popLocation(); + } + private int alignment(int value) { return 31 - Integer.numberOfLeadingZeros(Math.max(1, value)); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java index 912062063..89d28b63e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java @@ -36,8 +36,26 @@ public class WasmBinaryWriter { } if (type instanceof WasmType.Number) { writeType(((WasmType.Number) type).number); - } else if (type instanceof WasmType.Reference) { - writeSignedLEB(module.types.indexOf(((WasmType.Reference) type).composite)); + } else if (type instanceof WasmType.SpecialReference) { + switch (((WasmType.SpecialReference) type).kind) { + case ANY: + writeByte(0x6e); + break; + case EXTERN: + writeByte(0x6f); + break; + case FUNC: + writeByte(0x70); + break; + case STRUCT: + writeByte(0x6b); + break; + case ARRAY: + writeByte(0x6a); + break; + } + } else if (type instanceof WasmType.CompositeReference) { + writeSignedLEB(module.types.indexOf(((WasmType.CompositeReference) type).composite)); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java index b1920bd08..c2e53084d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java @@ -35,6 +35,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; @@ -48,6 +49,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatUnary; +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.WasmIndirectCall; @@ -72,6 +74,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStructGet; 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.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmThrow; @@ -783,6 +786,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { value = result; } + @Override + public void visit(WasmCallReference expression) { + unsupported(); + } + private void translateArguments(List wasmArguments, List signature, CExpression result, StringBuilder sb) { if (wasmArguments.isEmpty()) { @@ -1166,6 +1174,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { unsupported(); } + @Override + public void visit(WasmStructNewDefault expression) { + unsupported(); + } + @Override public void visit(WasmStructGet expression) { unsupported(); @@ -1196,6 +1209,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { unsupported(); } + @Override + public void visit(WasmFunctionReference expression) { + unsupported(); + } + private void unsupported() { value = new CExpression("/* unsupported */"); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index 58030ff61..b0db7d305 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -31,6 +31,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; @@ -47,6 +48,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatUnary; import org.teavm.backend.wasm.model.expression.WasmFloatUnaryOperation; +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.WasmIndirectCall; @@ -73,6 +75,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStructGet; 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.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmThrow; @@ -288,7 +291,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmNullConstant expression) { - open().append("ref.null " + module.types.indexOf(expression.getType())).close(); + open().append("ref.null " + type(expression.getType())).close(); } @Override @@ -446,6 +449,16 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { } + @Override + public void visit(WasmCallReference expression) { + open().append("call_ref ").append(type(expression.getType().getReference())); + line(expression.getFunctionReference()); + for (var argument : expression.getArguments()) { + line(argument); + } + close(); + } + @Override public void visit(WasmDrop expression) { open().append("drop").lf(); @@ -696,6 +709,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { close(); } + @Override + public void visit(WasmStructNewDefault expression) { + open().append("struct.new_default ").append(expression.getType().getReference()).close(); + } + @Override public void visit(WasmStructGet expression) { open(); @@ -773,11 +791,32 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { close(); } + @Override + public void visit(WasmFunctionReference expression) { + open().append("ref.func ").append(" $" + module.functions.indexOf(expression.getFunction())); + close(); + } + private String type(WasmType type) { if (type instanceof WasmType.Number) { return type(((WasmType.Number) type).number); - } else if (type instanceof WasmType.Reference) { - return "(ref " + typeName(((WasmType.Reference) type).composite) + ")"; + } else if (type instanceof WasmType.SpecialReference) { + switch (((WasmType.SpecialReference) type).kind) { + case ANY: + return "anyref"; + case EXTERN: + return "externref"; + case STRUCT: + return "structref"; + case FUNC: + return "funcref"; + case ARRAY: + return "arrayref"; + default: + throw new IllegalArgumentException(); + } + } else if (type instanceof WasmType.CompositeReference) { + return "(ref " + typeName(((WasmType.CompositeReference) type).composite) + ")"; } else { throw new IllegalArgumentException(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java index 4d3ce139a..b70bd2f75 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java @@ -24,6 +24,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; @@ -36,6 +37,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatUnary; +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.WasmIndirectCall; @@ -60,6 +62,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStructGet; 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.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmThrow; @@ -175,7 +178,7 @@ public class WasmTypeInference implements WasmExpressionVisitor { @Override public void visit(WasmNullConstant expression) { - result = expression.type.getReference(); + result = expression.type; } @Override @@ -189,6 +192,11 @@ public class WasmTypeInference implements WasmExpressionVisitor { result = expression.getType().getReturnType(); } + @Override + public void visit(WasmCallReference expression) { + result = expression.getType().getReturnType(); + } + @Override public void visit(WasmDrop expression) { result = null; @@ -274,6 +282,11 @@ public class WasmTypeInference implements WasmExpressionVisitor { result = expression.getType().getReference(); } + @Override + public void visit(WasmStructNewDefault expression) { + result = expression.getType().getReference(); + } + @Override public void visit(WasmStructGet expression) { result = expression.getType().getFields().get(expression.getFieldIndex()).asUnpackedType(); @@ -304,6 +317,11 @@ public class WasmTypeInference implements WasmExpressionVisitor { result = WasmType.INT32; } + @Override + public void visit(WasmFunctionReference expression) { + result = expression.getFunction().getType().getReference(); + } + private static WasmType map(WasmIntType type) { switch (type) { case INT32: diff --git a/core/src/main/java/org/teavm/model/util/ReflectionUtil.java b/core/src/main/java/org/teavm/model/util/ReflectionUtil.java new file mode 100644 index 000000000..9519cb169 --- /dev/null +++ b/core/src/main/java/org/teavm/model/util/ReflectionUtil.java @@ -0,0 +1,46 @@ +/* + * 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.model.util; + +import org.teavm.model.PrimitiveType; + +public final class ReflectionUtil { + private ReflectionUtil() { + } + + public static String typeName(PrimitiveType type) { + switch (type) { + case BOOLEAN: + return "boolean"; + case BYTE: + return "byte"; + case SHORT: + return "short"; + case CHARACTER: + return "char"; + case INTEGER: + return "int"; + case LONG: + return "long"; + case FLOAT: + return "float"; + case DOUBLE: + return "double"; + default: + throw new IllegalArgumentException(); + } + } +}