mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: general implementation of System.arraycopy
This commit is contained in:
parent
dd24425de0
commit
b9f406dcaa
|
@ -870,6 +870,15 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
|
|||
writer.eol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void arrayCopy(int targetTypeIndex, int sourceTypeIndex) {
|
||||
writer.address().write("array.copy ");
|
||||
writeTypeRef(targetTypeIndex);
|
||||
writer.write(" ");
|
||||
writeTypeRef(sourceTypeIndex);
|
||||
writer.eol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void functionReference(int functionIndex) {
|
||||
writer.address().write("ref.func ");
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.gc;
|
||||
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class SystemArrayCopyDependencySupport extends AbstractDependencyListener {
|
||||
private static final MethodReference COPY_METHOD = new MethodReference(System.class,
|
||||
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class);
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getMethod().getReference().equals(COPY_METHOD)) {
|
||||
var implMethod = agent.linkMethod(new MethodReference(System.class,
|
||||
"arrayCopyImpl", Object.class, int.class, Object.class, int.class, int.class, void.class));
|
||||
method.getVariable(1).connect(implMethod.getVariable(1));
|
||||
method.getVariable(3).connect(implMethod.getVariable(3));
|
||||
implMethod.use();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ public class WasmGCDependencies {
|
|||
contributeString();
|
||||
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
||||
analyzer.addDependencyListener(new WasmGCResourceDependency());
|
||||
analyzer.addDependencyListener(new SystemArrayCopyDependencySupport());
|
||||
}
|
||||
|
||||
public void contributeStandardExports() {
|
||||
|
|
|
@ -145,13 +145,16 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
private int enumConstantsFunctionOffset;
|
||||
private int arrayLengthOffset = -1;
|
||||
private int arrayGetOffset = -1;
|
||||
private int arrayCopyOffset = -1;
|
||||
private int cloneOffset = -1;
|
||||
private int servicesOffset = -1;
|
||||
private WasmStructure arrayVirtualTableStruct;
|
||||
private WasmFunction arrayGetObjectFunction;
|
||||
private WasmFunction arrayLengthObjectFunction;
|
||||
private WasmFunction arrayCopyObjectFunction;
|
||||
private WasmFunctionType arrayGetType;
|
||||
private WasmFunctionType arrayLengthType;
|
||||
private WasmFunctionType arrayCopyType;
|
||||
private List<WasmStructure> nonInitializedStructures = new ArrayList<>();
|
||||
private WasmArray objectArrayType;
|
||||
private boolean hasLoadServices;
|
||||
|
@ -176,7 +179,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
standardClasses = new WasmGCStandardClasses(this);
|
||||
strings = new WasmGCStringPool(standardClasses, module, functionProvider, names, functionTypes,
|
||||
dependencyInfo);
|
||||
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
|
||||
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes,
|
||||
queue);
|
||||
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names, queue);
|
||||
typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module);
|
||||
var customTypeMapperFactoryContext = customTypeMapperFactoryContext();
|
||||
|
@ -664,6 +668,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
WasmStructure objectStructure) {
|
||||
var virtualTable = virtualTables.lookup("java.lang.Object");
|
||||
var structure = getArrayVirtualTableStructure();
|
||||
structure.init();
|
||||
var itemType = ((ValueType.Array) type).getItemType();
|
||||
|
||||
for (var entry : virtualTable.getEntries()) {
|
||||
|
@ -681,9 +686,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayGetOffset,
|
||||
new WasmFunctionReference(getFunction)));
|
||||
}
|
||||
if (info.arrayCopy()) {
|
||||
var copyFunction = getArrayCopyFunction(itemType);
|
||||
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayCopyOffset,
|
||||
new WasmFunctionReference(copyFunction)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private WasmFunction getArrayLengthFunction(WasmStructure objectStructure) {
|
||||
var arrayTypeRef = (WasmType.CompositeReference) objectStructure.getFields().get(ARRAY_DATA_FIELD_OFFSET)
|
||||
.getUnpackedType();
|
||||
|
@ -815,6 +824,53 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction getArrayCopyFunction(ValueType itemType) {
|
||||
if (itemType instanceof ValueType.Primitive) {
|
||||
return createArrayCopyFunction(itemType);
|
||||
}
|
||||
return getArrayCopyObjectFunction();
|
||||
}
|
||||
|
||||
private WasmFunction getArrayCopyObjectFunction() {
|
||||
if (arrayCopyObjectFunction == null) {
|
||||
arrayCopyObjectFunction = createArrayCopyFunction(OBJECT_TYPE);
|
||||
}
|
||||
return arrayCopyObjectFunction;
|
||||
}
|
||||
|
||||
private WasmFunction createArrayCopyFunction(ValueType type) {
|
||||
var function = new WasmFunction(getArrayCopyType());
|
||||
function.setName(names.topLevel("Array<" + names.suggestForType(type) + ">::copy"));
|
||||
module.functions.add(function);
|
||||
function.setReferenced(true);
|
||||
|
||||
var arrayStruct = getClassInfo(ValueType.arrayOf(type)).structure;
|
||||
var arrayDataTypeRef = (WasmType.CompositeReference) arrayStruct.getFields()
|
||||
.get(ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
|
||||
var arrayDataType = (WasmArray) arrayDataTypeRef.composite;
|
||||
var sourceLocal = new WasmLocal(standardClasses.objectClass().getType(), "source");
|
||||
var sourceIndexLocal = new WasmLocal(WasmType.INT32, "sourceIndex");
|
||||
var targetLocal = new WasmLocal(standardClasses.objectClass().getType(), "target");
|
||||
var targetIndexLocal = new WasmLocal(WasmType.INT32, "targetIndex");
|
||||
var countLocal = new WasmLocal(WasmType.INT32, "count");
|
||||
function.add(sourceLocal);
|
||||
function.add(sourceIndexLocal);
|
||||
function.add(targetLocal);
|
||||
function.add(targetIndexLocal);
|
||||
function.add(countLocal);
|
||||
|
||||
var sourceArray = new WasmCast(new WasmGetLocal(sourceLocal), arrayStruct.getNonNullReference());
|
||||
var sourceArrayData = new WasmStructGet(arrayStruct, sourceArray, ARRAY_DATA_FIELD_OFFSET);
|
||||
var targetArray = new WasmCast(new WasmGetLocal(targetLocal), arrayStruct.getNonNullReference());
|
||||
var targetArrayData = new WasmStructGet(arrayStruct, targetArray, ARRAY_DATA_FIELD_OFFSET);
|
||||
|
||||
function.getBody().add(new WasmArrayCopy(
|
||||
arrayDataType, targetArrayData, new WasmGetLocal(sourceIndexLocal),
|
||||
arrayDataType, sourceArrayData, new WasmGetLocal(targetIndexLocal),
|
||||
new WasmGetLocal(countLocal)));
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunctionType getArrayGetType() {
|
||||
if (arrayGetType == null) {
|
||||
arrayGetType = functionTypes.of(standardClasses.objectClass().getType(),
|
||||
|
@ -830,6 +886,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
return arrayLengthType;
|
||||
}
|
||||
|
||||
private WasmFunctionType getArrayCopyType() {
|
||||
if (arrayCopyType == null) {
|
||||
arrayCopyType = functionTypes.of(null, standardClasses.objectClass().getType(), WasmType.INT32,
|
||||
standardClasses.objectClass().getType(), WasmType.INT32, WasmType.INT32);
|
||||
}
|
||||
return arrayCopyType;
|
||||
}
|
||||
|
||||
private void fillVirtualTableEntry(List<WasmExpression> target, WasmGlobal global,
|
||||
WasmStructure structure, WasmGCVirtualTable virtualTable, WasmGCVirtualTableEntry entry) {
|
||||
var implementor = virtualTable.implementor(entry);
|
||||
|
@ -966,6 +1030,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
fields.add(new WasmField(arrayGetType.getReference().asStorage(),
|
||||
names.structureField("@arrayGet")));
|
||||
}
|
||||
if (metadataRequirements.hasArrayCopy()) {
|
||||
arrayCopyOffset = fields.size();
|
||||
var arrayCopyType = getArrayCopyType();
|
||||
fields.add(new WasmField(arrayCopyType.getReference().asStorage(),
|
||||
names.structureField("@arrayCopy")));
|
||||
}
|
||||
});
|
||||
arrayVirtualTableStruct.setSupertype(standardClasses.objectClass().getVirtualTableStructure());
|
||||
module.types.add(arrayVirtualTableStruct);
|
||||
|
@ -986,6 +1056,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
return arrayGetOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArrayCopyOffset() {
|
||||
initStructures();
|
||||
return arrayCopyOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEnumConstantsFunctionOffset() {
|
||||
return enumConstantsFunctionOffset;
|
||||
|
|
|
@ -65,6 +65,8 @@ public interface WasmGCClassInfoProvider {
|
|||
|
||||
int getArrayLengthOffset();
|
||||
|
||||
int getArrayCopyOffset();
|
||||
|
||||
int getEnumConstantsFunctionOffset();
|
||||
|
||||
int getCloneOffset();
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Comparator;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
|
@ -51,19 +52,22 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
private TagRegistry tagRegistry;
|
||||
private WasmFunctionTypes functionTypes;
|
||||
private WasmFunctionType functionType;
|
||||
private Queue<Runnable> queue;
|
||||
|
||||
WasmGCSupertypeFunctionGenerator(
|
||||
WasmModule module,
|
||||
WasmGCClassGenerator classGenerator,
|
||||
WasmGCNameProvider nameProvider,
|
||||
TagRegistry tagRegistry,
|
||||
WasmFunctionTypes functionTypes
|
||||
WasmFunctionTypes functionTypes,
|
||||
Queue<Runnable> queue
|
||||
) {
|
||||
this.module = module;
|
||||
this.classGenerator = classGenerator;
|
||||
this.nameProvider = nameProvider;
|
||||
this.tagRegistry = tagRegistry;
|
||||
this.functionTypes = functionTypes;
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,17 +87,19 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
function.add(subtypeVar);
|
||||
module.functions.add(function);
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
var className = ((ValueType.Object) type).getClassName();
|
||||
generateIsClass(subtypeVar, className, function);
|
||||
} 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 WasmReferencesEqual(new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
|
||||
function.getBody().add(condition);
|
||||
}
|
||||
queue.add(() -> {
|
||||
if (type instanceof ValueType.Object) {
|
||||
var className = ((ValueType.Object) type).getClassName();
|
||||
generateIsClass(subtypeVar, className, function);
|
||||
} 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 WasmReferencesEqual(new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
|
||||
function.getBody().add(condition);
|
||||
}
|
||||
});
|
||||
|
||||
return function;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ 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.WasmStructure;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
|
||||
|
@ -822,5 +823,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
public WasmGCStringProvider strings() {
|
||||
return context.strings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmTag exceptionTag() {
|
||||
return context.getExceptionTag();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,20 +19,47 @@ import org.teavm.ast.InvocationExpr;
|
|||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||
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.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmStructure;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayCopy;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBranch;
|
||||
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.WasmExpression;
|
||||
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.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
||||
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
|
||||
private WasmFunction defaultFunction;
|
||||
private WasmFunction argsCheckFunction;
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "arraycopy":
|
||||
return generateArrayCopy(invocation, context);
|
||||
case "doArrayCopy":
|
||||
return generateDoArrayCopy(invocation, context);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private WasmExpression generateArrayCopy(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
var result = tryGenerateSpecialCase(invocation, context);
|
||||
if (result == null) {
|
||||
tryGenerateSpecialCase(invocation, context);
|
||||
|
@ -46,6 +73,32 @@ public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
|
|||
return result;
|
||||
}
|
||||
|
||||
private WasmExpression generateDoArrayCopy(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
var objStruct = context.classInfoProvider().getClassInfo(Object.class.getName()).getStructure();
|
||||
var arrayClsStruct = context.classInfoProvider().getArrayVirtualTableStructure();
|
||||
var block = new WasmBlock(false);
|
||||
|
||||
var source = context.exprCache().create(context.generate(invocation.getArguments().get(0)),
|
||||
objStruct.getReference(), invocation.getLocation(), block.getBody());
|
||||
WasmExpression sourceCls = new WasmStructGet(objStruct, source.expr(),
|
||||
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
|
||||
sourceCls = new WasmCast(sourceCls, arrayClsStruct.getNonNullReference());
|
||||
var copyFunction = new WasmStructGet(arrayClsStruct, sourceCls,
|
||||
context.classInfoProvider().getArrayCopyOffset());
|
||||
var functionTypeRef = (WasmType.CompositeReference) arrayClsStruct.getFields().get(
|
||||
context.classInfoProvider().getArrayCopyOffset()).getUnpackedType();
|
||||
var functionType = (WasmFunctionType) functionTypeRef.composite;
|
||||
var call = new WasmCallReference(copyFunction, functionType);
|
||||
call.getArguments().add(source.expr());
|
||||
call.getArguments().add(context.generate(invocation.getArguments().get(1)));
|
||||
call.getArguments().add(context.generate(invocation.getArguments().get(2)));
|
||||
call.getArguments().add(context.generate(invocation.getArguments().get(3)));
|
||||
call.getArguments().add(context.generate(invocation.getArguments().get(4)));
|
||||
block.getBody().add(call);
|
||||
source.release();
|
||||
return block;
|
||||
}
|
||||
|
||||
private WasmExpression tryGenerateSpecialCase(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
var sourceArray = invocation.getArguments().get(0);
|
||||
var targetArray = invocation.getArguments().get(2);
|
||||
|
@ -69,35 +122,120 @@ public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
|
|||
return null;
|
||||
}
|
||||
|
||||
var block = new WasmBlock(false);
|
||||
|
||||
var wasmTargetArrayType = (WasmType.CompositeReference) context.typeMapper().mapType(
|
||||
ValueType.arrayOf(targetItemType));
|
||||
var wasmTargetArrayStruct = (WasmStructure) wasmTargetArrayType.composite;
|
||||
var wasmTargetArrayWrapper = context.generate(invocation.getArguments().get(2));
|
||||
var wasmTargetArray = new WasmStructGet(wasmTargetArrayStruct, wasmTargetArrayWrapper,
|
||||
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET);
|
||||
var wasmTargetIndex = context.generate(invocation.getArguments().get(3));
|
||||
var wasmTargetArrayTypeRef = (WasmType.CompositeReference) wasmTargetArrayStruct.getFields()
|
||||
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
|
||||
var wasmTargetArray = context.exprCache().create(new WasmStructGet(wasmTargetArrayStruct,
|
||||
wasmTargetArrayWrapper, WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET),
|
||||
wasmTargetArrayTypeRef, null, block.getBody());
|
||||
var wasmTargetIndex = context.exprCache().create(context.generate(invocation.getArguments().get(3)),
|
||||
WasmType.INT32, null, block.getBody());
|
||||
var wasmSourceArrayType = (WasmType.CompositeReference) context.typeMapper().mapType(
|
||||
ValueType.arrayOf(sourceItemType));
|
||||
var wasmSourceArrayStruct = (WasmStructure) wasmSourceArrayType.composite;
|
||||
var wasmSourceArrayWrapper = context.generate(invocation.getArguments().get(0));
|
||||
var wasmSourceArray = new WasmStructGet(wasmSourceArrayStruct, wasmSourceArrayWrapper,
|
||||
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET);
|
||||
var wasmSourceIndex = context.generate(invocation.getArguments().get(1));
|
||||
var wasmSize = context.generate(invocation.getArguments().get(4));
|
||||
|
||||
var wasmTargetArrayTypeRef = (WasmType.CompositeReference) wasmTargetArrayStruct.getFields()
|
||||
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
|
||||
var wasmSourceArrayTypeRef = (WasmType.CompositeReference) wasmSourceArrayStruct.getFields()
|
||||
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
|
||||
var wasmSourceArray = context.exprCache().create(new WasmStructGet(wasmSourceArrayStruct,
|
||||
wasmSourceArrayWrapper, WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET),
|
||||
wasmSourceArrayTypeRef, null, block.getBody());
|
||||
var wasmSourceIndex = context.exprCache().create(context.generate(invocation.getArguments().get(1)),
|
||||
WasmType.INT32, null, block.getBody());
|
||||
var wasmSize = context.exprCache().create(context.generate(invocation.getArguments().get(4)),
|
||||
WasmType.INT32, null, block.getBody());
|
||||
|
||||
return new WasmArrayCopy((WasmArray) wasmTargetArrayTypeRef.composite, wasmTargetArray, wasmTargetIndex,
|
||||
(WasmArray) wasmSourceArrayTypeRef.composite, wasmSourceArray, wasmSourceIndex, wasmSize);
|
||||
|
||||
block.getBody().add(new WasmCall(
|
||||
getArgsCheckFunction(context),
|
||||
wasmTargetArray.expr(), wasmTargetIndex.expr(),
|
||||
wasmSourceArray.expr(), wasmSourceIndex.expr(),
|
||||
wasmSize.expr()
|
||||
));
|
||||
|
||||
block.getBody().add(new WasmArrayCopy(
|
||||
(WasmArray) wasmTargetArrayTypeRef.composite, wasmTargetArray.expr(), wasmTargetIndex.expr(),
|
||||
(WasmArray) wasmSourceArrayTypeRef.composite, wasmSourceArray.expr(), wasmSourceIndex.expr(),
|
||||
wasmSize.expr()
|
||||
));
|
||||
wasmTargetArray.release();
|
||||
wasmTargetIndex.release();
|
||||
wasmSourceArray.release();
|
||||
wasmSourceIndex.release();
|
||||
wasmSize.release();
|
||||
return block;
|
||||
}
|
||||
|
||||
private WasmFunction getArgsCheckFunction(WasmGCIntrinsicContext context) {
|
||||
if (argsCheckFunction == null) {
|
||||
argsCheckFunction = createArgsCheckFunction(context);
|
||||
}
|
||||
return argsCheckFunction;
|
||||
}
|
||||
|
||||
private WasmFunction createArgsCheckFunction(WasmGCIntrinsicContext context) {
|
||||
var function = new WasmFunction(context.functionTypes().of(null,
|
||||
WasmType.Reference.ARRAY, WasmType.INT32, WasmType.Reference.ARRAY,
|
||||
WasmType.INT32, WasmType.INT32));
|
||||
function.setName(context.names().topLevel("teavm@checkArrayCopy"));
|
||||
context.module().functions.add(function);
|
||||
|
||||
var targetArrayLocal = new WasmLocal(WasmType.Reference.ARRAY, "targetArray");
|
||||
var targetArrayIndexLocal = new WasmLocal(WasmType.INT32, "targetIndex");
|
||||
var sourceArrayLocal = new WasmLocal(WasmType.Reference.ARRAY, "sourceArray");
|
||||
var sourceArrayIndexLocal = new WasmLocal(WasmType.INT32, "sourceIndex");
|
||||
var countLocal = new WasmLocal(WasmType.INT32, "count");
|
||||
function.add(targetArrayLocal);
|
||||
function.add(targetArrayIndexLocal);
|
||||
function.add(sourceArrayLocal);
|
||||
function.add(sourceArrayIndexLocal);
|
||||
function.add(countLocal);
|
||||
|
||||
var block = new WasmBlock(false);
|
||||
var targetIndexLessThanZero = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(targetArrayIndexLocal), new WasmInt32Constant(0));
|
||||
block.getBody().add(new WasmBranch(targetIndexLessThanZero, block));
|
||||
var sourceIndexLessThanZero = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(sourceArrayIndexLocal), new WasmInt32Constant(0));
|
||||
block.getBody().add(new WasmBranch(sourceIndexLessThanZero, block));
|
||||
var countPositive = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(countLocal), new WasmInt32Constant(0));
|
||||
block.getBody().add(new WasmBranch(countPositive, block));
|
||||
|
||||
var targetSize = new WasmArrayLength(new WasmGetLocal(targetArrayLocal));
|
||||
var targetIndexLimit = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
|
||||
targetSize, new WasmGetLocal(countLocal));
|
||||
var targetIndexGreaterThanSize = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
|
||||
new WasmGetLocal(targetArrayIndexLocal), targetIndexLimit);
|
||||
block.getBody().add(new WasmBranch(targetIndexGreaterThanSize, block));
|
||||
|
||||
var sourceSize = new WasmArrayLength(new WasmGetLocal(sourceArrayLocal));
|
||||
var sourceIndexLimit = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
|
||||
sourceSize, new WasmGetLocal(countLocal));
|
||||
var sourceIndexGreaterThanSize = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
|
||||
new WasmGetLocal(sourceArrayIndexLocal), sourceIndexLimit);
|
||||
block.getBody().add(new WasmBranch(sourceIndexGreaterThanSize, block));
|
||||
|
||||
block.getBody().add(new WasmReturn());
|
||||
|
||||
function.getBody().add(block);
|
||||
|
||||
var aioobeFunction = context.functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "aiiobe",
|
||||
ArrayIndexOutOfBoundsException.class));
|
||||
var throwExpr = new WasmThrow(context.exceptionTag());
|
||||
throwExpr.getArguments().add(new WasmCall(aioobeFunction));
|
||||
function.getBody().add(throwExpr);
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction getDefaultFunction(WasmGCIntrinsicContext manager) {
|
||||
if (defaultFunction == null) {
|
||||
defaultFunction = manager.functions().forStaticMethod(new MethodReference(System.class,
|
||||
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class));
|
||||
"arrayCopyImpl", Object.class, int.class, Object.class, int.class, int.class, void.class));
|
||||
}
|
||||
return defaultFunction;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
|||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmTag;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
|
||||
|
@ -55,4 +56,6 @@ public interface WasmGCIntrinsicContext {
|
|||
WasmGCStringProvider strings();
|
||||
|
||||
ClassLoader classLoader();
|
||||
|
||||
WasmTag exceptionTag();
|
||||
}
|
||||
|
|
|
@ -108,8 +108,11 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
|||
}
|
||||
|
||||
private void fillSystem() {
|
||||
var arrayCopyIntrinsic = new SystemArrayCopyIntrinsic();
|
||||
add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class,
|
||||
int.class, int.class, void.class), new SystemArrayCopyIntrinsic());
|
||||
int.class, int.class, void.class), arrayCopyIntrinsic);
|
||||
add(new MethodReference(System.class, "doArrayCopy", Object.class, int.class, Object.class,
|
||||
int.class, int.class, void.class), arrayCopyIntrinsic);
|
||||
add(new MethodReference(System.class, "currentTimeMillis", long.class), new SystemIntrinsic());
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ public class WasmStructSet extends WasmExpression {
|
|||
private WasmExpression value;
|
||||
|
||||
public WasmStructSet(WasmStructure type, WasmExpression instance, int fieldIndex, WasmExpression value) {
|
||||
checkFieldIndex(fieldIndex);
|
||||
this.type = Objects.requireNonNull(type);
|
||||
this.instance = Objects.requireNonNull(instance);
|
||||
this.fieldIndex = fieldIndex;
|
||||
|
@ -52,6 +53,7 @@ public class WasmStructSet extends WasmExpression {
|
|||
}
|
||||
|
||||
public void setFieldIndex(int fieldIndex) {
|
||||
checkFieldIndex(fieldIndex);
|
||||
this.fieldIndex = fieldIndex;
|
||||
}
|
||||
|
||||
|
@ -67,4 +69,11 @@ public class WasmStructSet extends WasmExpression {
|
|||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
private static void checkFieldIndex(int fieldIndex) {
|
||||
if (fieldIndex < 0) {
|
||||
throw new IllegalArgumentException("Field index must be >= 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,11 +83,15 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
|
|||
}
|
||||
}
|
||||
} else if (cls.getName().equals("java.lang.System")) {
|
||||
Program arrayCopyProgram = null;
|
||||
MethodDescriptor arrayCopyDescriptor = null;
|
||||
for (var method : cls.getMethods()) {
|
||||
switch (method.getName()) {
|
||||
case "arraycopy":
|
||||
arrayCopyProgram = method.getProgram();
|
||||
method.setProgram(null);
|
||||
method.getModifiers().add(ElementModifier.NATIVE);
|
||||
arrayCopyDescriptor = method.getDescriptor();
|
||||
break;
|
||||
case "currentTimeMillis": {
|
||||
var annotation = new AnnotationHolder(Import.class.getName());
|
||||
|
@ -97,6 +101,13 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (arrayCopyProgram != null) {
|
||||
var arrayCopyImpl = new MethodHolder(new MethodDescriptor("arrayCopyImpl",
|
||||
arrayCopyDescriptor.getSignature()));
|
||||
arrayCopyImpl.setProgram(arrayCopyProgram);
|
||||
arrayCopyImpl.getModifiers().add(ElementModifier.STATIC);
|
||||
cls.addMethod(arrayCopyImpl);
|
||||
}
|
||||
} else if (cls.getName().equals("java.lang.ref.WeakReference")) {
|
||||
var constructor = cls.getMethod(new MethodDescriptor("<init>", Object.class, ReferenceQueue.class,
|
||||
void.class));
|
||||
|
|
|
@ -42,10 +42,13 @@ public class ClassMetadataRequirements {
|
|||
"get", Object.class, int.class, Object.class);
|
||||
private static final MethodReference ARRAY_LENGTH = new MethodReference(Array.class,
|
||||
"getLength", Object.class, int.class);
|
||||
private static final MethodReference ARRAY_COPY = new MethodReference(System.class,
|
||||
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class);
|
||||
private static final ClassInfo EMPTY_INFO = new ClassInfo();
|
||||
private Map<ValueType, ClassInfo> requirements = new HashMap<>();
|
||||
private boolean hasArrayGet;
|
||||
private boolean hasArrayLength;
|
||||
private boolean hasArrayCopy;
|
||||
private boolean hasEnumConstants;
|
||||
private boolean hasSuperclass;
|
||||
private boolean hasIsAssignable;
|
||||
|
@ -137,6 +140,15 @@ public class ClassMetadataRequirements {
|
|||
}
|
||||
}
|
||||
|
||||
var arrayCopy = dependencyInfo.getMethod(ARRAY_COPY);
|
||||
if (arrayCopy != null) {
|
||||
hasArrayCopy = arrayCopy.isUsed();
|
||||
var classNames = arrayCopy.getVariable(1).getTypes();
|
||||
for (var className : classNames) {
|
||||
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).arrayCopy = true;
|
||||
}
|
||||
}
|
||||
|
||||
var clone = dependencyInfo.getMethod(new MethodReference(Object.class, "cloneObject", Object.class));
|
||||
if (clone != null) {
|
||||
var classNames = clone.getVariable(0).getTypes();
|
||||
|
@ -182,6 +194,10 @@ public class ClassMetadataRequirements {
|
|||
return hasArrayLength;
|
||||
}
|
||||
|
||||
public boolean hasArrayCopy() {
|
||||
return hasArrayCopy;
|
||||
}
|
||||
|
||||
public boolean hasEnumConstants() {
|
||||
return hasEnumConstants;
|
||||
}
|
||||
|
@ -240,6 +256,7 @@ public class ClassMetadataRequirements {
|
|||
boolean newArray;
|
||||
boolean arrayLength;
|
||||
boolean arrayGet;
|
||||
boolean arrayCopy;
|
||||
boolean cloneMethod;
|
||||
boolean enumConstants;
|
||||
|
||||
|
@ -283,6 +300,11 @@ public class ClassMetadataRequirements {
|
|||
return arrayLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arrayCopy() {
|
||||
return arrayCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean arrayGet() {
|
||||
return arrayGet;
|
||||
|
@ -318,6 +340,8 @@ public class ClassMetadataRequirements {
|
|||
|
||||
boolean arrayGet();
|
||||
|
||||
boolean arrayCopy();
|
||||
|
||||
boolean cloneMethod();
|
||||
|
||||
boolean enumConstants();
|
||||
|
|
Loading…
Reference in New Issue
Block a user