mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: implement String.intern
This commit is contained in:
parent
8ed8322b17
commit
4546029a5a
|
@ -28,6 +28,7 @@ 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.WasmSetGlobal;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
@ -168,6 +169,23 @@ public class WasmGCModuleGenerator {
|
|||
return caller;
|
||||
}
|
||||
|
||||
public WasmFunction generateReportGarbageCollectedStringFunction() {
|
||||
var entryType = ValueType.object(StringInternPool.class.getName() + "$Entry");
|
||||
var function = declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
StringInternPool.class.getName(),
|
||||
"remove",
|
||||
entryType,
|
||||
ValueType.VOID
|
||||
));
|
||||
var caller = new WasmFunction(function.getType());
|
||||
var entryLocal = new WasmLocal(declarationsGenerator.typeMapper().mapType(entryType));
|
||||
caller.add(entryLocal);
|
||||
caller.getBody().add(callInitializer());
|
||||
caller.getBody().add(new WasmCall(function, new WasmGetLocal(entryLocal)));
|
||||
declarationsGenerator.module.functions.add(caller);
|
||||
return caller;
|
||||
}
|
||||
|
||||
private void createInitializer() {
|
||||
if (initializer != null) {
|
||||
return;
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.teavm.backend.wasm.render.WasmBinaryRenderer;
|
|||
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
|
@ -209,6 +210,13 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
var charAtFunction = moduleGenerator.generateCharAtFunction();
|
||||
charAtFunction.setExportName("charAt");
|
||||
|
||||
var internMethod = controller.getDependencyInfo().getMethod(new MethodReference(String.class,
|
||||
"intern", String.class));
|
||||
if (internMethod != null && internMethod.isUsed()) {
|
||||
var removeStringEntryFunction = moduleGenerator.generateReportGarbageCollectedStringFunction();
|
||||
removeStringEntryFunction.setExportName("reportGarbageCollectedString");
|
||||
}
|
||||
|
||||
moduleGenerator.generate();
|
||||
adjustModuleMemory(module);
|
||||
|
||||
|
@ -257,4 +265,12 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
public boolean needsSystemArrayCopyOptimization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterClassInitializer(String initializer) {
|
||||
if (initializer.equals(StringInternPool.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.gc;
|
||||
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class StringInternDependencySupport extends AbstractDependencyListener {
|
||||
private static final MethodReference STRING_INTERN = new MethodReference(String.class, "intern", String.class);
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getMethod().getReference().equals(STRING_INTERN)) {
|
||||
var query = agent.linkMethod(new MethodReference(StringInternPool.class, "query",
|
||||
String.class, String.class));
|
||||
query.getVariable(1).propagate(agent.getType("java.lang.String"));
|
||||
query.use();
|
||||
|
||||
var entryTypeName = StringInternPool.class.getName() + "$Entry";
|
||||
var remove = agent.linkMethod(new MethodReference(
|
||||
StringInternPool.class.getName(),
|
||||
"remove",
|
||||
ValueType.object(entryTypeName),
|
||||
ValueType.VOID));
|
||||
remove.getVariable(1).propagate(agent.getType(entryTypeName));
|
||||
remove.use();
|
||||
|
||||
var clinit = agent.linkMethod(new MethodReference(StringInternPool.class, "<clinit>",
|
||||
void.class));
|
||||
clinit.use();
|
||||
|
||||
var getValue = agent.linkMethod(new MethodReference(StringInternPool.class.getName() + "$Entry",
|
||||
"getValue", ValueType.parse(String.class)));
|
||||
getValue.getResult().propagate(agent.getType(String.class.getName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ public class WasmGCDependencies {
|
|||
contributeMathUtils();
|
||||
contributeExceptionUtils();
|
||||
contributeInitializerUtils();
|
||||
contributeString();
|
||||
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
||||
}
|
||||
|
||||
|
@ -107,4 +108,8 @@ public class WasmGCDependencies {
|
|||
private void contributeInitializerUtils() {
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "nextCharArray", char[].class)).use();
|
||||
}
|
||||
|
||||
private void contributeString() {
|
||||
analyzer.addDependencyListener(new StringInternDependencySupport());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ 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.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
|
@ -173,7 +174,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
this.names = names;
|
||||
this.classInitializerInfo = classInitializerInfo;
|
||||
standardClasses = new WasmGCStandardClasses(this);
|
||||
strings = new WasmGCStringPool(standardClasses, module, functionProvider, names, functionTypes);
|
||||
strings = new WasmGCStringPool(standardClasses, module, functionProvider, names, functionTypes,
|
||||
dependencyInfo);
|
||||
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
|
||||
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names, queue);
|
||||
typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module);
|
||||
|
@ -1138,6 +1140,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
fields.add(wasmField);
|
||||
}
|
||||
}
|
||||
if (className.equals(StringInternPool.class.getName() + "$Entry")) {
|
||||
var field = new WasmField(WasmType.Reference.EXTERN.asStorage(), "nativeRef");
|
||||
fields.add(field);
|
||||
}
|
||||
if (className.equals("java.lang.Class")) {
|
||||
var cls = classSource.get("java.lang.Class");
|
||||
classFlagsOffset = fields.size();
|
||||
|
|
|
@ -27,6 +27,7 @@ public interface WasmGCClassInfoProvider {
|
|||
int CUSTOM_FIELD_OFFSETS = 2;
|
||||
int ARRAY_DATA_FIELD_OFFSET = 2;
|
||||
int WEAK_REFERENCE_OFFSET = 2;
|
||||
int STRING_POOL_ENTRY_OFFSET = 5;
|
||||
|
||||
WasmGCClassInfo getClassInfo(ValueType type);
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
|||
import org.teavm.backend.wasm.model.expression.WasmTest;
|
||||
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
||||
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
|
@ -539,6 +540,9 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
|
||||
@Override
|
||||
protected boolean needsClassInitializer(String className) {
|
||||
if (className.equals(StringInternPool.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
var printWriter = new PrintWriter(buffer);
|
||||
e.printStackTrace(printWriter);
|
||||
diagnostics.error(new CallLocation(method.getReference()),
|
||||
"Failed generating method body due to internal exception: " + buffer.toString());
|
||||
"Failed generating method body due to internal exception: " + buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,15 @@ import org.teavm.backend.wasm.model.WasmLocal;
|
|||
import org.teavm.backend.wasm.model.WasmMemorySegment;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor {
|
||||
|
@ -46,15 +49,17 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
private WasmFunction initNextStringFunction;
|
||||
private WasmGCNameProvider names;
|
||||
private WasmFunctionTypes functionTypes;
|
||||
private DependencyInfo dependencyInfo;
|
||||
|
||||
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
|
||||
BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names,
|
||||
WasmFunctionTypes functionTypes) {
|
||||
WasmFunctionTypes functionTypes, DependencyInfo dependencyInfo) {
|
||||
this.standardClasses = standardClasses;
|
||||
this.module = module;
|
||||
this.functionProvider = functionProvider;
|
||||
this.names = names;
|
||||
this.functionTypes = functionTypes;
|
||||
this.dependencyInfo = dependencyInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,7 +74,11 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
if (initNextStringFunction == null) {
|
||||
return;
|
||||
}
|
||||
var stringStruct = standardClasses.stringClass().getStructure();
|
||||
if (hasIntern()) {
|
||||
var internInit = functionProvider.forStaticMethod(new MethodReference(StringInternPool.class, "<clinit>",
|
||||
void.class));
|
||||
function.getBody().add(new WasmCall(internInit));
|
||||
}
|
||||
for (var str : stringMap.values()) {
|
||||
function.getBody().add(new WasmCall(initNextStringFunction, new WasmGetGlobal(str.global)));
|
||||
}
|
||||
|
@ -127,7 +136,19 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
function.getBody().add(new WasmStructSet(stringTypeInfo.getStructure(), new WasmGetLocal(stringLocal),
|
||||
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET,
|
||||
new WasmGetGlobal(stringTypeInfo.getPointer())));
|
||||
if (hasIntern()) {
|
||||
var queryFunction = functionProvider.forStaticMethod(new MethodReference(StringInternPool.class,
|
||||
"query", String.class, String.class));
|
||||
function.getBody().add(new WasmDrop(new WasmCall(queryFunction, new WasmGetLocal(stringLocal))));
|
||||
functionProvider.forStaticMethod(new MethodReference(StringInternPool.class, "<clinit>",
|
||||
void.class));
|
||||
}
|
||||
module.functions.add(function);
|
||||
initNextStringFunction = function;
|
||||
}
|
||||
|
||||
private boolean hasIntern() {
|
||||
var intern = dependencyInfo.getMethod(new MethodReference(String.class, "intern", String.class));
|
||||
return intern != null && intern.isUsed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.generators.gc;
|
||||
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class StringGenerator implements WasmGCCustomGenerator {
|
||||
@Override
|
||||
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
|
||||
var worker = context.functions().forStaticMethod(new MethodReference(StringInternPool.class,
|
||||
"query", String.class, String.class));
|
||||
var instanceLocal = new WasmLocal(context.typeMapper().mapType(ValueType.parse(String.class)), "this");
|
||||
function.add(instanceLocal);
|
||||
function.getBody().add(new WasmCall(worker, new WasmGetLocal(instanceLocal)));
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
fillSystem();
|
||||
fillArray();
|
||||
fillWeakReference();
|
||||
fillString();
|
||||
for (var entry : generators.entrySet()) {
|
||||
add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
@ -79,6 +80,11 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
add(new MethodReference(WeakReference.class, "clear", void.class), generator);
|
||||
}
|
||||
|
||||
private void fillString() {
|
||||
var generator = new StringGenerator();
|
||||
add(new MethodReference(String.class, "intern", String.class), generator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmGCCustomGenerator get(MethodReference method) {
|
||||
var result = generators.get(method);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.intrinsics.gc;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
class StringInternPoolIntrinsic implements WasmGCIntrinsic {
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
var entryStruct = context.classInfoProvider().getClassInfo(StringInternPool.class.getName() + "$Entry")
|
||||
.getStructure();
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getValue": {
|
||||
var weakRef = new WasmStructGet(entryStruct, context.generate(invocation.getArguments().get(0)),
|
||||
WasmGCClassInfoProvider.STRING_POOL_ENTRY_OFFSET);
|
||||
return new WasmCall(createDerefFunction(context), weakRef);
|
||||
}
|
||||
case "setValue": {
|
||||
var block = new WasmBlock(false);
|
||||
var instance = context.exprCache().create(context.generate(invocation.getArguments().get(0)),
|
||||
entryStruct.getReference(), invocation.getLocation(), block.getBody());
|
||||
var value = context.generate(invocation.getArguments().get(1));
|
||||
var ref = new WasmCall(createRefFunction(context), value, instance.expr());
|
||||
block.getBody().add(new WasmStructSet(entryStruct, instance.expr(),
|
||||
WasmGCClassInfoProvider.STRING_POOL_ENTRY_OFFSET, ref));
|
||||
instance.release();
|
||||
return block;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private WasmFunction createRefFunction(WasmGCIntrinsicContext context) {
|
||||
var function = new WasmFunction(context.functionTypes().of(
|
||||
WasmType.Reference.EXTERN,
|
||||
context.typeMapper().mapType(ValueType.parse(String.class)),
|
||||
context.typeMapper().mapType(ValueType.object(StringInternPool.class.getName() + "$Entry"))
|
||||
));
|
||||
function.setName(context.names().topLevel("teavm@stringRef"));
|
||||
function.setImportModule("teavm");
|
||||
function.setImportName("createStringWeakRef");
|
||||
context.module().functions.add(function);
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction createDerefFunction(WasmGCIntrinsicContext context) {
|
||||
var function = new WasmFunction(context.functionTypes().of(
|
||||
context.typeMapper().mapType(ValueType.parse(String.class)),
|
||||
WasmType.Reference.EXTERN
|
||||
));
|
||||
function.setName(context.names().topLevel("teavm@stringDeref"));
|
||||
function.setImportModule("teavm");
|
||||
function.setImportName("stringDeref");
|
||||
context.module().functions.add(function);
|
||||
return function;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import java.util.Map;
|
|||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -38,7 +39,6 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
|||
this.classes = classes;
|
||||
this.services = services;
|
||||
this.factories = List.copyOf(factories);
|
||||
factories = List.copyOf(factories);
|
||||
fillWasmRuntime();
|
||||
fillObject();
|
||||
fillClass();
|
||||
|
@ -48,6 +48,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
|||
fillFloat();
|
||||
fillDouble();
|
||||
fillArray();
|
||||
fillString();
|
||||
for (var entry : customIntrinsics.entrySet()) {
|
||||
add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
@ -148,6 +149,13 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
|||
add(new MethodReference(Array.class, "getImpl", Object.class, int.class, Object.class), intrinsic);
|
||||
}
|
||||
|
||||
private void fillString() {
|
||||
var intrinsic = new StringInternPoolIntrinsic();
|
||||
var className = StringInternPool.class.getName() + "$Entry";
|
||||
add(new MethodReference(className, "getValue", ValueType.parse(String.class)), intrinsic);
|
||||
add(new MethodReference(className, "setValue", ValueType.parse(String.class), ValueType.VOID), intrinsic);
|
||||
}
|
||||
|
||||
private void add(MethodReference methodRef, WasmGCIntrinsic intrinsic) {
|
||||
intrinsics.put(methodRef, new IntrinsicContainer(intrinsic));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.runtime;
|
||||
|
||||
public final class StringInternPool {
|
||||
private static Entry[] table = new Entry[16];
|
||||
private static int occupiedCells;
|
||||
private static int occupiedCellsThreshold = table.length * 3 / 4;
|
||||
|
||||
private StringInternPool() {
|
||||
}
|
||||
|
||||
public static String query(String s) {
|
||||
var hash = s.hashCode();
|
||||
var index = Integer.remainderUnsigned(hash, table.length);
|
||||
var entry = table[index];
|
||||
while (entry != null) {
|
||||
if (entry.hash == hash) {
|
||||
var value = entry.getValue();
|
||||
if (value.equals(s)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
entry = entry.next;
|
||||
}
|
||||
|
||||
if (table[index] == null) {
|
||||
if (++occupiedCells > occupiedCellsThreshold) {
|
||||
rehash();
|
||||
}
|
||||
index = Integer.remainderUnsigned(hash, table.length);
|
||||
if (table[index] == null) {
|
||||
++occupiedCells;
|
||||
}
|
||||
}
|
||||
table[index] = new Entry(index, hash, table[index], s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void rehash() {
|
||||
var oldTable = table;
|
||||
table = new Entry[oldTable.length * 2];
|
||||
occupiedCells = 0;
|
||||
occupiedCellsThreshold = table.length * 3 / 4;
|
||||
for (var value : oldTable) {
|
||||
var entry = value;
|
||||
while (entry != null) {
|
||||
var next = entry.next;
|
||||
var index = Integer.remainderUnsigned(entry.hash, table.length);
|
||||
entry.index = index;
|
||||
entry.next = table[index];
|
||||
if (entry.next == null) {
|
||||
++occupiedCells;
|
||||
}
|
||||
table[index] = entry;
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void remove(Entry entry) {
|
||||
var index = entry.index;
|
||||
Entry previous = null;
|
||||
var e = table[index];
|
||||
while (e != null) {
|
||||
if (e == entry) {
|
||||
if (previous == null) {
|
||||
table[index] = e.next;
|
||||
if (e.next == null) {
|
||||
--occupiedCells;
|
||||
}
|
||||
} else {
|
||||
previous.next = e.next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
previous = e;
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
|
||||
static class Entry {
|
||||
int index;
|
||||
final int hash;
|
||||
Entry next;
|
||||
|
||||
Entry(int index, int hash, Entry next, String str) {
|
||||
this.index = index;
|
||||
this.hash = hash;
|
||||
this.next = next;
|
||||
setValue(str);
|
||||
}
|
||||
|
||||
final native String getValue();
|
||||
|
||||
final native void setValue(String str);
|
||||
}
|
||||
}
|
|
@ -574,11 +574,13 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
BasicBlock block = program.basicBlockAt(0);
|
||||
Instruction first = block.getFirstInstruction();
|
||||
for (String className : classInitializerInfo.getInitializationOrder()) {
|
||||
if (target.filterClassInitializer(className)) {
|
||||
var invoke = new InvokeInstruction();
|
||||
invoke.setMethod(new MethodReference(className, CLINIT_DESC));
|
||||
first.insertPrevious(invoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
||||
Linker linker = new Linker(dependency);
|
||||
|
|
|
@ -70,4 +70,8 @@ public interface TeaVMTarget {
|
|||
default boolean needsSystemArrayCopyOptimization() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean filterClassInitializer(String initializer) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@ TeaVM.wasm = function() {
|
|||
exports.reportGarbageCollectedValue(heldValue)
|
||||
}
|
||||
});
|
||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
exports.reportGarbageCollectedString(heldValue);
|
||||
});
|
||||
imports.teavm = {
|
||||
putcharStderr(c) {
|
||||
if (c === 10) {
|
||||
|
@ -57,6 +60,14 @@ TeaVM.wasm = function() {
|
|||
},
|
||||
deref(weakRef) {
|
||||
return weakRef.deref();
|
||||
},
|
||||
createStringWeakRef(value, heldValue) {
|
||||
let weakRef = new WeakRef(value);
|
||||
stringFinalizationRegistry.register(value, heldValue)
|
||||
return weakRef;
|
||||
},
|
||||
stringDeref(weakRef) {
|
||||
return weakRef.deref();
|
||||
}
|
||||
};
|
||||
imports.teavmMath = Math;
|
||||
|
|
Loading…
Reference in New Issue
Block a user