wasm gc: fix generation issues, get first version that produces module that passes validation

This commit is contained in:
Alexey Andreev 2024-08-01 19:57:12 +02:00
parent a8d97ad387
commit 54dc7fe5f8
17 changed files with 302 additions and 129 deletions

View File

@ -57,6 +57,7 @@ public class WasmGCModuleGenerator {
var mainFunction = declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint, var mainFunction = declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint,
"main", ValueType.parse(String[].class), ValueType.VOID)); "main", ValueType.parse(String[].class), ValueType.VOID));
var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null)); var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null));
declarationsGenerator.module.functions.add(mainFunctionCaller);
mainFunctionCaller.getBody().add(callInitializer()); mainFunctionCaller.getBody().add(callInitializer());
var tempVars = new TemporaryVariablePool(mainFunctionCaller); var tempVars = new TemporaryVariablePool(mainFunctionCaller);
@ -80,6 +81,7 @@ public class WasmGCModuleGenerator {
} }
var functionType = declarationsGenerator.functionTypes.of(null); var functionType = declarationsGenerator.functionTypes.of(null);
initializer = new WasmFunction(functionType); initializer = new WasmFunction(functionType);
initializer.setReferenced(true);
declarationsGenerator.module.functions.add(initializer); declarationsGenerator.module.functions.add(initializer);
initializerRef = new WasmGlobal("teavm_initializer", functionType.getReference(), initializerRef = new WasmGlobal("teavm_initializer", functionType.getReference(),
new WasmFunctionReference(initializer)); new WasmFunctionReference(initializer));

View File

@ -123,10 +123,25 @@ public class WasmGCTarget implements TeaVMTarget {
mainFunction.setExportName(controller.getEntryPointName()); mainFunction.setExportName(controller.getEntryPointName());
mainFunction.setName(controller.getEntryPointName()); mainFunction.setName(controller.getEntryPointName());
moduleGenerator.generate(); moduleGenerator.generate();
adjustModuleMemory(module);
emitWasmFile(module, buildTarget, outputName); emitWasmFile(module, buildTarget, outputName);
} }
private void adjustModuleMemory(WasmModule module) {
var memorySize = 0;
for (var segment : module.getSegments()) {
memorySize = Math.max(memorySize, segment.getOffset() + segment.getLength());
}
if (memorySize == 0) {
return;
}
var pages = (memorySize - 1) / WasmHeap.PAGE_SIZE + 1;
module.setMinMemorySize(pages);
module.setMaxMemorySize(pages);
}
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException { private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
var binaryWriter = new WasmBinaryWriter(); var binaryWriter = new WasmBinaryWriter();
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated, var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,

View File

@ -22,8 +22,9 @@ import org.teavm.model.ValueType;
import org.teavm.model.analysis.BaseTypeInference; import org.teavm.model.analysis.BaseTypeInference;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
public class PreciseTypeInference extends BaseTypeInference<ValueType> { public class PreciseTypeInference extends BaseTypeInference<PreciseValueType> {
public static final ValueType OBJECT_TYPE = ValueType.object("java.lang.Object"); public static final PreciseValueType OBJECT_TYPE = new PreciseValueType(ValueType.object("java.lang.Object"),
false);
private ClassHierarchy hierarchy; private ClassHierarchy hierarchy;
private WasmGCMethodReturnTypes returnTypes; private WasmGCMethodReturnTypes returnTypes;
@ -35,42 +36,42 @@ public class PreciseTypeInference extends BaseTypeInference<ValueType> {
} }
@Override @Override
protected ValueType mapType(ValueType type) { protected PreciseValueType mapType(ValueType type) {
return type; return new PreciseValueType(type, false);
} }
@Override @Override
protected ValueType nullType() { protected PreciseValueType nullType() {
return null; return null;
} }
@Override @Override
protected ValueType merge(ValueType a, ValueType b) { protected PreciseValueType merge(PreciseValueType a, PreciseValueType b) {
if (a == null) { if (a == null) {
return b; return b;
} else if (b == null) { } else if (b == null) {
return a; return a;
} else { } else {
if (a instanceof ValueType.Primitive && b instanceof ValueType.Primitive) { if (a.valueType instanceof ValueType.Primitive && b.valueType instanceof ValueType.Primitive) {
if (a != b) { if (a.valueType != b.valueType) {
return OBJECT_TYPE; return OBJECT_TYPE;
} else { } else {
return a; return a;
} }
} else if (a instanceof ValueType.Array) { } else if (a.valueType instanceof ValueType.Array) {
if (b instanceof ValueType.Array) { if (b.valueType instanceof ValueType.Array) {
var p = ((ValueType.Array) a).getItemType(); var p = new PreciseValueType(((ValueType.Array) a.valueType).getItemType(), false);
var q = ((ValueType.Array) b).getItemType(); var q = new PreciseValueType(((ValueType.Array) b.valueType).getItemType(), false);
return ValueType.arrayOf(merge(p, q)); return new PreciseValueType(ValueType.arrayOf(merge(p, q).valueType), a.isArrayUnwrap);
} else { } else {
return OBJECT_TYPE; return OBJECT_TYPE;
} }
} else if (b instanceof ValueType.Array) { } else if (b.valueType instanceof ValueType.Array) {
return OBJECT_TYPE; return OBJECT_TYPE;
} else if (a instanceof ValueType.Object) { } else if (a.valueType instanceof ValueType.Object) {
if (b instanceof ValueType.Object) { if (b.valueType instanceof ValueType.Object) {
var p = ((ValueType.Object) a).getClassName(); var p = ((ValueType.Object) a.valueType).getClassName();
var q = ((ValueType.Object) b).getClassName(); var q = ((ValueType.Object) b.valueType).getClassName();
if (p.equals(q)) { if (p.equals(q)) {
return a; return a;
} }
@ -87,7 +88,8 @@ public class PreciseTypeInference extends BaseTypeInference<ValueType> {
} else if (hierarchy.isSuperType(q, p, false)) { } else if (hierarchy.isSuperType(q, p, false)) {
return b; return b;
} }
return ValueType.object(WasmGCUtil.findCommonSuperclass(hierarchy, first, second)); var result = ValueType.object(WasmGCUtil.findCommonSuperclass(hierarchy, first, second));
return new PreciseValueType(result, false);
} else { } else {
return OBJECT_TYPE; return OBJECT_TYPE;
} }
@ -97,15 +99,20 @@ public class PreciseTypeInference extends BaseTypeInference<ValueType> {
} }
} }
@Override @Override
protected ValueType elementType(ValueType valueType) { protected PreciseValueType elementType(PreciseValueType valueType) {
return ((ValueType.Array) valueType).getItemType(); return new PreciseValueType(((ValueType.Array) valueType.valueType).getItemType(), false);
} }
@Override @Override
protected ValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) { protected PreciseValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) {
if (invocationType == InvocationType.SPECIAL) { if (invocationType == InvocationType.SPECIAL) {
return returnTypes.returnTypeOf(methodRef); return new PreciseValueType(returnTypes.returnTypeOf(methodRef), false);
} }
return super.methodReturnType(invocationType, methodRef); return super.methodReturnType(invocationType, methodRef);
} }
@Override
protected PreciseValueType arrayUnwrapType(PreciseValueType type) {
return new PreciseValueType(type.valueType, true);
}
} }

View File

@ -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.gc;
import org.teavm.model.ValueType;
public class PreciseValueType {
public final ValueType valueType;
public final boolean isArrayUnwrap;
public PreciseValueType(ValueType valueType, boolean isArrayUnwrap) {
this.valueType = valueType;
this.isArrayUnwrap = isArrayUnwrap;
}
}

View File

@ -17,7 +17,6 @@ package org.teavm.backend.wasm.gc;
import java.util.Arrays; import java.util.Arrays;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -52,12 +51,14 @@ public class WasmGCDependencies {
} }
private void contributeExceptionUtils() { private void contributeExceptionUtils() {
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class)); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class))
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class)); .use();
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class))
.use();
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use();
} }
private void contributeInitializerUtils() { private void contributeInitializerUtils() {
analyzer.linkMethod(new MethodReference(WasmGCStringPool.class, "nextCharArray", char[].class)); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "nextCharArray", char[].class)).use();
} }
} }

View File

@ -37,6 +37,7 @@ public class WasmGCVariableCategoryProvider implements VariableCategoryProvider
@Override @Override
public Object[] getCategories(Program program, MethodReference method) { public Object[] getCategories(Program program, MethodReference method) {
inference = new PreciseTypeInference(program, method, hierarchy, returnTypes); inference = new PreciseTypeInference(program, method, hierarchy, returnTypes);
inference.setPhisSkipped(true);
var result = new Object[program.variableCount()]; var result = new Object[program.variableCount()];
for (int i = 0; i < program.variableCount(); ++i) { for (int i = 0; i < program.variableCount(); ++i) {
var type = inference.typeOf(program.variableAt(i)); var type = inference.typeOf(program.variableAt(i));

View File

@ -95,7 +95,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int classTagOffset; private int classTagOffset;
private int classFlagsOffset; private int classFlagsOffset;
private int classNameOffset; private int classNameOffset = -1;
private int classParentOffset; private int classParentOffset;
private int classArrayOffset; private int classArrayOffset;
private int classArrayItemOffset; private int classArrayItemOffset;
@ -141,7 +141,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
@Override @Override
public void contributeToInitializerDefinitions(WasmFunction function) { public void contributeToInitializerDefinitions(WasmFunction function) {
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
var newStruct = new WasmStructNewDefault(standardClasses.classClass().getStructure()); var classInstanceType = classInfo.virtualTableStructure != null
? classInfo.virtualTableStructure
: standardClasses.classClass().getStructure();
var newStruct = new WasmStructNewDefault(classInstanceType);
function.getBody().add(new WasmSetGlobal(classInfo.pointer, newStruct)); function.getBody().add(new WasmSetGlobal(classInfo.pointer, newStruct));
} }
} }
@ -153,6 +156,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
initializerFunctionStatements.clear(); initializerFunctionStatements.clear();
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
supertypeFunction.setReferenced(true);
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
new WasmFunctionReference(supertypeFunction))); new WasmFunctionReference(supertypeFunction)));
function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET,
@ -161,6 +165,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var className = ((ValueType.Object) classInfo.getValueType()).getClassName(); var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
var initFunction = functionProvider.forStaticMethod(new MethodReference(className, var initFunction = functionProvider.forStaticMethod(new MethodReference(className,
CLINIT_METHOD_DESC)); CLINIT_METHOD_DESC));
initFunction.setReferenced(true);
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer, function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
new WasmFunctionReference(initFunction))); new WasmFunctionReference(initFunction)));
} }
@ -191,7 +196,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classInfo.structure.setSupertype(standardClasses.objectClass().structure); classInfo.structure.setSupertype(standardClasses.objectClass().structure);
} }
module.types.add(classInfo.structure); module.types.add(classInfo.structure);
fillFields(classInfo.structure.getFields(), type); fillFields(classInfo, type);
} }
var pointerName = names.forClassInstance(type); var pointerName = names.forClassInstance(type);
var classStructure = type instanceof ValueType.Object var classStructure = type instanceof ValueType.Object
@ -331,6 +336,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
wrapperFunction.getLocalVariables().addAll(List.of(params)); wrapperFunction.getLocalVariables().addAll(List.of(params));
} }
function.setReferenced(true);
var ref = new WasmFunctionReference(function); var ref = new WasmFunctionReference(function);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref)); target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
} }
@ -439,13 +445,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return result; return result;
} }
private void fillFields(List<WasmStorageType> fields, ValueType type) { private void fillFields(WasmGCClassInfo classInfo, ValueType type) {
var fields = classInfo.structure.getFields();
fields.add(standardClasses.classClass().getType().asStorage()); fields.add(standardClasses.classClass().getType().asStorage());
fields.add(WasmType.Reference.ANY.asStorage()); fields.add(WasmType.Reference.ANY.asStorage());
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
fillClassFields(fields, ((ValueType.Object) type).getClassName()); fillClassFields(fields, ((ValueType.Object) type).getClassName());
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
fillArrayField(fields, ((ValueType.Array) type).getItemType()); fillArrayField(classInfo, ((ValueType.Array) type).getItemType());
} }
} }
@ -487,14 +494,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classSupertypeFunctionOffset = fields.size(); classSupertypeFunctionOffset = fields.size();
fields.add(supertypeGenerator.getFunctionType().getReference().asStorage()); fields.add(supertypeGenerator.getFunctionType().getReference().asStorage());
virtualTableFieldOffset = fields.size(); virtualTableFieldOffset = fields.size();
classNameOffset = fieldIndexes.get(new FieldReference(className, "name")); classNameOffset = fieldIndexes.getOrDefault(new FieldReference(className, "name"), -1);
} }
} }
private void fillArrayField(List<WasmStorageType> fields, ValueType elementType) { private void fillArrayField(WasmGCClassInfo classInfo, ValueType elementType) {
var wasmArray = new WasmArray(null, () -> typeMapper.mapStorageType(elementType)); var wasmArray = new WasmArray(null, () -> typeMapper.mapStorageType(elementType));
module.types.add(wasmArray); module.types.add(wasmArray);
fields.add(wasmArray.getReference().asStorage()); classInfo.structure.getFields().add(wasmArray.getReference().asStorage());
classInfo.array = wasmArray;
} }
private WasmFunction getCreatePrimitiveClassFunction() { private WasmFunction getCreatePrimitiveClassFunction() {
@ -512,11 +520,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
WasmType.INT32 WasmType.INT32
); );
var function = new WasmFunction(functionType); var function = new WasmFunction(functionType);
function.setName("_teavm_fill_primitive_class_"); function.setName("teavm_fill_primitive_class");
module.functions.add(function); module.functions.add(function);
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name"); var nameVar = new WasmLocal(standardClasses.stringClass().getType(), "name");
var kindVar = new WasmLocal(WasmType.INT32, "kind"); var kindVar = new WasmLocal(WasmType.INT32, "kind");
function.add(targetVar); function.add(targetVar);
function.add(nameVar); function.add(nameVar);
@ -540,12 +548,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classFlagsOffset, classFlagsOffset,
flagsExpr flagsExpr
)); ));
function.getBody().add(new WasmStructSet( if (classNameOffset >= 0) {
standardClasses.classClass().getStructure(), function.getBody().add(new WasmStructSet(
new WasmGetLocal(targetVar), standardClasses.classClass().getStructure(),
classNameOffset, new WasmGetLocal(targetVar),
new WasmGetLocal(nameVar) classNameOffset,
)); new WasmGetLocal(nameVar)
));
}
function.getBody().add(new WasmStructSet( function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(), standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar), new WasmGetLocal(targetVar),
@ -559,7 +569,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (createArrayClassFunction == null) { if (createArrayClassFunction == null) {
createArrayClassFunction = createCreateArrayClassFunction(); createArrayClassFunction = createCreateArrayClassFunction();
} }
return createCreateArrayClassFunction(); return createArrayClassFunction;
} }
private WasmFunction createCreateArrayClassFunction() { private WasmFunction createCreateArrayClassFunction() {
@ -570,7 +580,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
); );
var function = new WasmFunction(functionType); var function = new WasmFunction(functionType);
module.functions.add(function); module.functions.add(function);
function.setName("_teavm_fill_array_class_"); function.setName("teavm_fill_array_class");
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item"); var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");

View File

@ -17,6 +17,7 @@ package org.teavm.backend.wasm.generate.gc.classes;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -26,6 +27,7 @@ import org.teavm.model.ValueType;
public class WasmGCClassInfo { public class WasmGCClassInfo {
private ValueType valueType; private ValueType valueType;
WasmStructure structure; WasmStructure structure;
WasmArray array;
WasmStructure virtualTableStructure; WasmStructure virtualTableStructure;
WasmGlobal pointer; WasmGlobal pointer;
WasmGlobal initializerPointer; WasmGlobal initializerPointer;
@ -43,6 +45,10 @@ public class WasmGCClassInfo {
return structure; return structure;
} }
public WasmArray getArray() {
return array;
}
public WasmStructure getVirtualTableStructure() { public WasmStructure getVirtualTableStructure() {
return virtualTableStructure; return virtualTableStructure;
} }

View File

@ -35,8 +35,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary; import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
@ -79,27 +79,27 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
private WasmFunction generateIsSupertypeFunction(ValueType type) { private WasmFunction generateIsSupertypeFunction(ValueType type) {
var function = new WasmFunction(getFunctionType()); var function = new WasmFunction(getFunctionType());
function.setName(nameProvider.forSupertypeFunction(type)); function.setName(nameProvider.forSupertypeFunction(type));
var subtypeVar = new WasmLocal(WasmType.INT32, "subtype"); var subtypeVar = new WasmLocal(classGenerator.standardClasses.classClass().getType(), "subtype");
function.add(subtypeVar); function.add(subtypeVar);
module.functions.add(function); module.functions.add(function);
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
var className = ((ValueType.Object) type).getClassName(); var className = ((ValueType.Object) type).getClassName();
generateIsClass(subtypeVar, className, function.getBody()); generateIsClass(subtypeVar, className, function);
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
ValueType itemType = ((ValueType.Array) type).getItemType(); ValueType itemType = ((ValueType.Array) type).getItemType();
generateIsArray(subtypeVar, itemType, function.getBody()); generateIsArray(subtypeVar, itemType, function.getBody());
} else { } else {
var expected = classGenerator.getClassInfo(type).pointer; var expected = classGenerator.getClassInfo(type).pointer;
var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, var condition = new WasmReferencesEqual(new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
function.getBody().add(new WasmReturn(condition)); function.getBody().add(new WasmReturn(condition));
} }
return function; return function;
} }
private void generateIsClass(WasmLocal subtypeVar, String className, List<WasmExpression> body) { private void generateIsClass(WasmLocal subtypeVar, String className, WasmFunction function) {
var body = function.getBody();
var ranges = tagRegistry.getRanges(className); var ranges = tagRegistry.getRanges(className);
if (ranges.isEmpty()) { if (ranges.isEmpty()) {
body.add(new WasmReturn(new WasmInt32Constant(0))); body.add(new WasmReturn(new WasmInt32Constant(0)));
@ -108,8 +108,10 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
int tagOffset = classGenerator.getClassTagOffset(); int tagOffset = classGenerator.getClassTagOffset();
var tagVar = new WasmLocal(WasmType.INT32, "tag");
function.add(tagVar);
var tagExpression = getClassField(new WasmGetLocal(subtypeVar), tagOffset); var tagExpression = getClassField(new WasmGetLocal(subtypeVar), tagOffset);
body.add(new WasmSetLocal(subtypeVar, tagExpression)); body.add(new WasmSetLocal(tagVar, tagExpression));
ranges.sort(Comparator.comparingInt(range -> range.lower)); ranges.sort(Comparator.comparingInt(range -> range.lower));
@ -117,13 +119,13 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
int upper = ranges.get(ranges.size() - 1).upper; int upper = ranges.get(ranges.size() - 1).upper;
var lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, var lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower)); new WasmGetLocal(tagVar), new WasmInt32Constant(lower));
var testLower = new WasmConditional(lowerCondition); var testLower = new WasmConditional(lowerCondition);
testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
body.add(testLower); body.add(testLower);
var upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, var upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper)); new WasmGetLocal(tagVar), new WasmInt32Constant(upper));
var testUpper = new WasmConditional(upperCondition); var testUpper = new WasmConditional(upperCondition);
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
body.add(testUpper); body.add(testUpper);
@ -133,12 +135,12 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
int upperHole = ranges.get(i).lower; int upperHole = ranges.get(i).lower;
lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole)); new WasmGetLocal(tagVar), new WasmInt32Constant(lowerHole));
testLower = new WasmConditional(lowerCondition); testLower = new WasmConditional(lowerCondition);
body.add(testLower); body.add(testLower);
upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upperHole)); new WasmGetLocal(tagVar), new WasmInt32Constant(upperHole));
testUpper = new WasmConditional(upperCondition); testUpper = new WasmConditional(upperCondition);
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
@ -154,8 +156,8 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset); var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset);
body.add(new WasmSetLocal(subtypeVar, itemExpression)); body.add(new WasmSetLocal(subtypeVar, itemExpression));
var itemTest = new WasmConditional(new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, var itemTest = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(subtypeVar),
new WasmGetLocal(subtypeVar))); new WasmNullConstant(WasmType.Reference.STRUCT)));
itemTest.setType(WasmType.INT32); itemTest.setType(WasmType.INT32);
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));

View File

@ -48,6 +48,7 @@ import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmSignedType;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
@ -340,7 +341,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
public void visit(SubscriptExpr expr) { public void visit(SubscriptExpr expr) {
accept(expr.getArray()); accept(expr.getArray());
var arrayData = unwrapArray(result); var arrayData = result;
arrayData.acceptVisitor(typeInference); arrayData.acceptVisitor(typeInference);
var arrayTypeRef = (WasmType.CompositeReference) typeInference.getResult(); var arrayTypeRef = (WasmType.CompositeReference) typeInference.getResult();
var arrayType = (WasmArray) arrayTypeRef.composite; var arrayType = (WasmArray) arrayTypeRef.composite;
@ -348,8 +349,22 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
accept(expr.getIndex()); accept(expr.getIndex());
var index = result; var index = result;
result = new WasmArrayGet(arrayType, arrayData, index); var arrayGet = new WasmArrayGet(arrayType, arrayData, index);
result.setLocation(expr.getLocation()); switch (expr.getType()) {
case BYTE:
arrayGet.setSignedType(WasmSignedType.SIGNED);
break;
case SHORT:
arrayGet.setSignedType(WasmSignedType.SIGNED);
break;
case CHAR:
arrayGet.setSignedType(WasmSignedType.UNSIGNED);
break;
default:
break;
}
arrayGet.setLocation(expr.getLocation());
result = arrayGet;
} }
@Override @Override
@ -395,11 +410,32 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
target.acceptVisitor(typeInference); target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult(); var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite; var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField()); var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField());
var structGet = new WasmStructGet(struct, target, fieldIndex);
result = new WasmStructGet(struct, target, fieldIndex); var cls = context.classes().get(expr.getField().getClassName());
result.setLocation(expr.getLocation()); if (cls != null) {
var field = cls.getField(expr.getField().getFieldName());
if (field != null) {
var fieldType = field.getType();
if (fieldType instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) fieldType).getKind()) {
case BYTE:
structGet.setSignedType(WasmSignedType.SIGNED);
break;
case SHORT:
structGet.setSignedType(WasmSignedType.SIGNED);
break;
case CHARACTER:
structGet.setSignedType(WasmSignedType.UNSIGNED);
break;
default:
break;
}
}
}
}
structGet.setLocation(expr.getLocation());
result = structGet;
} }
} }

View File

@ -240,7 +240,10 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
for (var i = firstVar; i < ast.getVariables().size(); ++i) { for (var i = firstVar; i < ast.getVariables().size(); ++i) {
var localVar = ast.getVariables().get(i); var localVar = ast.getVariables().get(i);
var representative = method.getProgram().variableAt(variableRepresentatives[i]); var representative = method.getProgram().variableAt(variableRepresentatives[i]);
var type = typeMapper.mapType(typeInference.typeOf(representative)); var inferredType = typeInference.typeOf(representative);
var type = !inferredType.isArrayUnwrap
? typeMapper.mapType(inferredType.valueType)
: classInfoProvider.getClassInfo(inferredType.valueType).getArray().getReference();
var wasmLocal = new WasmLocal(type, localVar.getName()); var wasmLocal = new WasmLocal(type, localVar.getName());
function.add(wasmLocal); function.add(wasmLocal);
} }
@ -304,6 +307,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
dummyInitializer = new WasmFunction(functionTypes.of(null)); dummyInitializer = new WasmFunction(functionTypes.of(null));
dummyInitializer.getBody().add(new WasmReturn()); dummyInitializer.getBody().add(new WasmReturn());
dummyInitializer.setName("teavm_dummy_initializer"); dummyInitializer.setName("teavm_dummy_initializer");
dummyInitializer.setReferenced(true);
module.functions.add(dummyInitializer); module.functions.add(dummyInitializer);
} }
return dummyInitializer; return dummyInitializer;

View File

@ -33,6 +33,7 @@ import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor { public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor {
@ -41,6 +42,7 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter(); private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>(); private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
private BaseWasmFunctionRepository functionProvider; private BaseWasmFunctionRepository functionProvider;
private WasmFunction nextCharArrayFunction;
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module, public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
BaseWasmFunctionRepository functionProvider) { BaseWasmFunctionRepository functionProvider) {
@ -62,8 +64,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
@Override @Override
public void contributeToInitializer(WasmFunction function) { public void contributeToInitializer(WasmFunction function) {
var nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCStringPool.class, if (nextCharArrayFunction == null) {
"nextCharArray", char[].class)); return;
}
var stringStruct = standardClasses.stringClass().getStructure(); var stringStruct = standardClasses.stringClass().getStructure();
for (var str : stringMap.values()) { for (var str : stringMap.values()) {
var value = new WasmCall(nextCharArrayFunction); var value = new WasmCall(nextCharArrayFunction);
@ -75,9 +78,12 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
@Override @Override
public WasmGCStringConstant getStringConstant(String string) { public WasmGCStringConstant getStringConstant(String string) {
return stringMap.computeIfAbsent(string, s -> { return stringMap.computeIfAbsent(string, s -> {
if (nextCharArrayFunction == null) {
initNextCharArrayFunction();
}
binaryWriter.writeInt32(string.length()); binaryWriter.writeInt32(string.length());
binaryWriter.writeBytes(string.getBytes(StandardCharsets.UTF_8)); binaryWriter.writeBytes(string.getBytes(StandardCharsets.UTF_8));
var globalName = "_teavm_java_string_" + stringMap.size(); var globalName = "teavm_java_string_" + stringMap.size();
var globalType = standardClasses.stringClass().getType(); var globalType = standardClasses.stringClass().getType();
var global = new WasmGlobal(globalName, globalType, WasmExpression.defaultValueOfType(globalType)); var global = new WasmGlobal(globalName, globalType, WasmExpression.defaultValueOfType(globalType));
module.globals.add(global); module.globals.add(global);
@ -85,50 +91,8 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
}); });
} }
static char[] nextCharArray() { private void initNextCharArrayFunction() {
var length = nextLEB(); nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCSupport.class,
var result = new char[length]; "nextCharArray", char[].class));
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();
} }

View File

@ -18,7 +18,7 @@ package org.teavm.backend.wasm.generators.gc;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider; import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
@ -30,7 +30,7 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
private void fillStringPool() { private void fillStringPool() {
generators.put( generators.put(
new MethodReference(WasmGCStringPool.class, "nextByte", byte.class), new MethodReference(WasmGCSupport.class, "nextByte", byte.class),
new WasmGCStringPoolGenerator() new WasmGCStringPoolGenerator()
); );
} }

View File

@ -27,6 +27,7 @@ public class WasmFunction extends WasmEntity {
private String exportName; private String exportName;
private String importName; private String importName;
private String importModule; private String importModule;
private boolean referenced;
private WasmFunctionType type; private WasmFunctionType type;
private List<WasmLocal> localVariables = new ArrayList<>(); private List<WasmLocal> localVariables = new ArrayList<>();
private List<WasmLocal> readonlyLocalVariables = Collections.unmodifiableList(localVariables); private List<WasmLocal> readonlyLocalVariables = Collections.unmodifiableList(localVariables);
@ -70,6 +71,14 @@ public class WasmFunction extends WasmEntity {
this.importModule = importModule; this.importModule = importModule;
} }
public boolean isReferenced() {
return referenced;
}
public void setReferenced(boolean referenced) {
this.referenced = referenced;
}
@Override @Override
boolean isImported() { boolean isImported() {
return importName != null; return importName != null;

View File

@ -265,19 +265,39 @@ public class WasmBinaryRenderer {
} }
private void renderElement(WasmModule module) { private void renderElement(WasmModule module) {
if (module.getFunctionTable().isEmpty()) { var count = 0;
if (!module.getFunctionTable().isEmpty()) {
++count;
}
if (module.functions.stream().anyMatch(WasmFunction::isReferenced)) {
++count;
}
if (count == 0) {
return; return;
} }
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(1); section.writeLEB(count);
section.writeLEB(0);
renderInitializer(section, 0); if (!module.getFunctionTable().isEmpty()) {
section.writeLEB(0);
renderInitializer(section, 0);
section.writeLEB(module.getFunctionTable().size());
for (var function : module.getFunctionTable()) {
section.writeLEB(module.functions.indexOf(function));
}
}
section.writeLEB(module.getFunctionTable().size()); var referencedFunctions = module.functions.stream()
for (var function : module.getFunctionTable()) { .filter(WasmFunction::isReferenced)
section.writeLEB(module.functions.indexOf(function)); .collect(Collectors.toList());
if (!referencedFunctions.isEmpty()) {
section.writeLEB(3);
section.writeByte(0);
section.writeLEB(referencedFunctions.size());
for (var function : referencedFunctions) {
section.writeLEB(module.functions.indexOf(function));
}
} }
writeSection(SECTION_ELEMENT, "element", section.getData()); writeSection(SECTION_ELEMENT, "element", section.getData());

View File

@ -38,4 +38,51 @@ public class WasmGCSupport {
@Import(name = "putcharStderr") @Import(name = "putcharStderr")
public static native void putCharStderr(char c); public static native void putCharStderr(char c);
public 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();
} }

View File

@ -60,12 +60,18 @@ public abstract class BaseTypeInference<T> {
private Object[] types; private Object[] types;
private Graph graph; private Graph graph;
private Graph arrayGraph; private Graph arrayGraph;
private Graph arrayUnwrapGraph;
private boolean phisSkipped;
public BaseTypeInference(Program program, MethodReference reference) { public BaseTypeInference(Program program, MethodReference reference) {
this.program = program; this.program = program;
this.reference = reference; this.reference = reference;
} }
public void setPhisSkipped(boolean phisSkipped) {
this.phisSkipped = phisSkipped;
}
private void prepare() { private void prepare() {
types = new Object[program.variableCount()]; types = new Object[program.variableCount()];
var visitor = new InitialTypeVisitor(program.variableCount()); var visitor = new InitialTypeVisitor(program.variableCount());
@ -78,9 +84,11 @@ public abstract class BaseTypeInference<T> {
for (var insn : block) { for (var insn : block) {
insn.acceptVisitor(visitor); insn.acceptVisitor(visitor);
} }
for (var phi : block.getPhis()) { if (!phisSkipped) {
for (var incoming : phi.getIncomings()) { for (var phi : block.getPhis()) {
visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex()); for (var incoming : phi.getIncomings()) {
visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
}
} }
} }
for (var tryCatch : block.getTryCatchBlocks()) { for (var tryCatch : block.getTryCatchBlocks()) {
@ -95,6 +103,7 @@ public abstract class BaseTypeInference<T> {
} }
graph = visitor.graphBuilder.build(); graph = visitor.graphBuilder.build();
arrayGraph = visitor.arrayGraphBuilder.build(); arrayGraph = visitor.arrayGraphBuilder.build();
arrayUnwrapGraph = visitor.arrayUnwrapGraphBuilder.build();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -126,6 +135,12 @@ public abstract class BaseTypeInference<T> {
typeStack.push(type); typeStack.push(type);
} }
} }
for (var succ : arrayUnwrapGraph.outgoingEdges(variable)) {
if (!Objects.equals(types[succ], type)) {
stack.push(succ);
typeStack.push(arrayUnwrapType(type));
}
}
if (arrayGraph.outgoingEdgesCount(variable) > 0) { if (arrayGraph.outgoingEdgesCount(variable) > 0) {
var elementType = elementType(type); var elementType = elementType(type);
for (var succ : arrayGraph.outgoingEdges(variable)) { for (var succ : arrayGraph.outgoingEdges(variable)) {
@ -174,13 +189,19 @@ public abstract class BaseTypeInference<T> {
return mapType(methodRef.getReturnType()); return mapType(methodRef.getReturnType());
} }
protected T arrayUnwrapType(T type) {
return type;
}
private class InitialTypeVisitor extends AbstractInstructionVisitor { private class InitialTypeVisitor extends AbstractInstructionVisitor {
private GraphBuilder graphBuilder; private GraphBuilder graphBuilder;
private GraphBuilder arrayGraphBuilder; private GraphBuilder arrayGraphBuilder;
private GraphBuilder arrayUnwrapGraphBuilder;
InitialTypeVisitor(int size) { InitialTypeVisitor(int size) {
graphBuilder = new GraphBuilder(size); graphBuilder = new GraphBuilder(size);
arrayGraphBuilder = new GraphBuilder(size); arrayGraphBuilder = new GraphBuilder(size);
arrayUnwrapGraphBuilder = new GraphBuilder(size);
} }
@Override @Override
@ -309,7 +330,7 @@ public abstract class BaseTypeInference<T> {
@Override @Override
public void visit(UnwrapArrayInstruction insn) { public void visit(UnwrapArrayInstruction insn) {
graphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex()); arrayUnwrapGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
} }
@Override @Override