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.WasmGetGlobal;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
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.backend.wasm.runtime.WasmGCSupport;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
@ -168,6 +169,23 @@ public class WasmGCModuleGenerator {
|
||||||
return caller;
|
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() {
|
private void createInitializer() {
|
||||||
if (initializer != null) {
|
if (initializer != null) {
|
||||||
return;
|
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.WasmBinaryStatsCollector;
|
||||||
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
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.backend.wasm.transformation.gc.BaseClassesTransformation;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
|
@ -209,6 +210,13 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
var charAtFunction = moduleGenerator.generateCharAtFunction();
|
var charAtFunction = moduleGenerator.generateCharAtFunction();
|
||||||
charAtFunction.setExportName("charAt");
|
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();
|
moduleGenerator.generate();
|
||||||
adjustModuleMemory(module);
|
adjustModuleMemory(module);
|
||||||
|
|
||||||
|
@ -257,4 +265,12 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
public boolean needsSystemArrayCopyOptimization() {
|
public boolean needsSystemArrayCopyOptimization() {
|
||||||
return false;
|
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();
|
contributeMathUtils();
|
||||||
contributeExceptionUtils();
|
contributeExceptionUtils();
|
||||||
contributeInitializerUtils();
|
contributeInitializerUtils();
|
||||||
|
contributeString();
|
||||||
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,4 +108,8 @@ public class WasmGCDependencies {
|
||||||
private void contributeInitializerUtils() {
|
private void contributeInitializerUtils() {
|
||||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "nextCharArray", char[].class)).use();
|
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.WasmStructNew;
|
||||||
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.runtime.StringInternPool;
|
||||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.model.ClassHierarchy;
|
import org.teavm.model.ClassHierarchy;
|
||||||
|
@ -173,7 +174,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
this.names = names;
|
this.names = names;
|
||||||
this.classInitializerInfo = classInitializerInfo;
|
this.classInitializerInfo = classInitializerInfo;
|
||||||
standardClasses = new WasmGCStandardClasses(this);
|
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);
|
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
|
||||||
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names, queue);
|
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names, queue);
|
||||||
typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module);
|
typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module);
|
||||||
|
@ -1138,6 +1140,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
fields.add(wasmField);
|
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")) {
|
if (className.equals("java.lang.Class")) {
|
||||||
var cls = classSource.get("java.lang.Class");
|
var cls = classSource.get("java.lang.Class");
|
||||||
classFlagsOffset = fields.size();
|
classFlagsOffset = fields.size();
|
||||||
|
|
|
@ -27,6 +27,7 @@ public interface WasmGCClassInfoProvider {
|
||||||
int CUSTOM_FIELD_OFFSETS = 2;
|
int CUSTOM_FIELD_OFFSETS = 2;
|
||||||
int ARRAY_DATA_FIELD_OFFSET = 2;
|
int ARRAY_DATA_FIELD_OFFSET = 2;
|
||||||
int WEAK_REFERENCE_OFFSET = 2;
|
int WEAK_REFERENCE_OFFSET = 2;
|
||||||
|
int STRING_POOL_ENTRY_OFFSET = 5;
|
||||||
|
|
||||||
WasmGCClassInfo getClassInfo(ValueType type);
|
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.WasmTest;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
import org.teavm.backend.wasm.model.expression.WasmThrow;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||||
|
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||||
import org.teavm.model.ClassHierarchy;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
@ -539,6 +540,9 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean needsClassInitializer(String className) {
|
protected boolean needsClassInitializer(String className) {
|
||||||
|
if (className.equals(StringInternPool.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;
|
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
var printWriter = new PrintWriter(buffer);
|
var printWriter = new PrintWriter(buffer);
|
||||||
e.printStackTrace(printWriter);
|
e.printStackTrace(printWriter);
|
||||||
diagnostics.error(new CallLocation(method.getReference()),
|
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.WasmMemorySegment;
|
||||||
import org.teavm.backend.wasm.model.WasmModule;
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
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.WasmGetGlobal;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||||
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.StringInternPool;
|
||||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor {
|
public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor {
|
||||||
|
@ -46,15 +49,17 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
||||||
private WasmFunction initNextStringFunction;
|
private WasmFunction initNextStringFunction;
|
||||||
private WasmGCNameProvider names;
|
private WasmGCNameProvider names;
|
||||||
private WasmFunctionTypes functionTypes;
|
private WasmFunctionTypes functionTypes;
|
||||||
|
private DependencyInfo dependencyInfo;
|
||||||
|
|
||||||
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
|
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
|
||||||
BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names,
|
BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names,
|
||||||
WasmFunctionTypes functionTypes) {
|
WasmFunctionTypes functionTypes, DependencyInfo dependencyInfo) {
|
||||||
this.standardClasses = standardClasses;
|
this.standardClasses = standardClasses;
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.functionProvider = functionProvider;
|
this.functionProvider = functionProvider;
|
||||||
this.names = names;
|
this.names = names;
|
||||||
this.functionTypes = functionTypes;
|
this.functionTypes = functionTypes;
|
||||||
|
this.dependencyInfo = dependencyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,7 +74,11 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
||||||
if (initNextStringFunction == null) {
|
if (initNextStringFunction == null) {
|
||||||
return;
|
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()) {
|
for (var str : stringMap.values()) {
|
||||||
function.getBody().add(new WasmCall(initNextStringFunction, new WasmGetGlobal(str.global)));
|
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),
|
function.getBody().add(new WasmStructSet(stringTypeInfo.getStructure(), new WasmGetLocal(stringLocal),
|
||||||
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET,
|
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET,
|
||||||
new WasmGetGlobal(stringTypeInfo.getPointer())));
|
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);
|
module.functions.add(function);
|
||||||
initNextStringFunction = 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();
|
fillSystem();
|
||||||
fillArray();
|
fillArray();
|
||||||
fillWeakReference();
|
fillWeakReference();
|
||||||
|
fillString();
|
||||||
for (var entry : generators.entrySet()) {
|
for (var entry : generators.entrySet()) {
|
||||||
add(entry.getKey(), entry.getValue());
|
add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
@ -79,6 +80,11 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
||||||
add(new MethodReference(WeakReference.class, "clear", void.class), generator);
|
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
|
@Override
|
||||||
public WasmGCCustomGenerator get(MethodReference method) {
|
public WasmGCCustomGenerator get(MethodReference method) {
|
||||||
var result = generators.get(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.WasmRuntime;
|
||||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider;
|
import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
|
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -38,7 +39,6 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
this.services = services;
|
this.services = services;
|
||||||
this.factories = List.copyOf(factories);
|
this.factories = List.copyOf(factories);
|
||||||
factories = List.copyOf(factories);
|
|
||||||
fillWasmRuntime();
|
fillWasmRuntime();
|
||||||
fillObject();
|
fillObject();
|
||||||
fillClass();
|
fillClass();
|
||||||
|
@ -48,6 +48,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
||||||
fillFloat();
|
fillFloat();
|
||||||
fillDouble();
|
fillDouble();
|
||||||
fillArray();
|
fillArray();
|
||||||
|
fillString();
|
||||||
for (var entry : customIntrinsics.entrySet()) {
|
for (var entry : customIntrinsics.entrySet()) {
|
||||||
add(entry.getKey(), entry.getValue());
|
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);
|
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) {
|
private void add(MethodReference methodRef, WasmGCIntrinsic intrinsic) {
|
||||||
intrinsics.put(methodRef, new IntrinsicContainer(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);
|
BasicBlock block = program.basicBlockAt(0);
|
||||||
Instruction first = block.getFirstInstruction();
|
Instruction first = block.getFirstInstruction();
|
||||||
for (String className : classInitializerInfo.getInitializationOrder()) {
|
for (String className : classInitializerInfo.getInitializationOrder()) {
|
||||||
|
if (target.filterClassInitializer(className)) {
|
||||||
var invoke = new InvokeInstruction();
|
var invoke = new InvokeInstruction();
|
||||||
invoke.setMethod(new MethodReference(className, CLINIT_DESC));
|
invoke.setMethod(new MethodReference(className, CLINIT_DESC));
|
||||||
first.insertPrevious(invoke);
|
first.insertPrevious(invoke);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
||||||
Linker linker = new Linker(dependency);
|
Linker linker = new Linker(dependency);
|
||||||
|
|
|
@ -70,4 +70,8 @@ public interface TeaVMTarget {
|
||||||
default boolean needsSystemArrayCopyOptimization() {
|
default boolean needsSystemArrayCopyOptimization() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean filterClassInitializer(String initializer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ TeaVM.wasm = function() {
|
||||||
exports.reportGarbageCollectedValue(heldValue)
|
exports.reportGarbageCollectedValue(heldValue)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||||
|
exports.reportGarbageCollectedString(heldValue);
|
||||||
|
});
|
||||||
imports.teavm = {
|
imports.teavm = {
|
||||||
putcharStderr(c) {
|
putcharStderr(c) {
|
||||||
if (c === 10) {
|
if (c === 10) {
|
||||||
|
@ -57,6 +60,14 @@ TeaVM.wasm = function() {
|
||||||
},
|
},
|
||||||
deref(weakRef) {
|
deref(weakRef) {
|
||||||
return weakRef.deref();
|
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;
|
imports.teavmMath = Math;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user