mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
parent
2513ceb236
commit
5bc398415d
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 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.classlib.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.common.DisjointSet;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
|
||||
public class ClassForNameTransformer implements ClassHolderTransformer {
|
||||
private static final MethodReference getNameMethod = new MethodReference(Class.class, "getName", String.class);
|
||||
private static final MethodReference forNameMethod = new MethodReference(Class.class, "forName", String.class,
|
||||
boolean.class, ClassLoader.class, Class.class);
|
||||
private static final MethodReference forNameShortMethod = new MethodReference(Class.class, "forName",
|
||||
String.class, Class.class);
|
||||
private static final MethodReference initMethod = new MethodReference(Class.class, "initialize", void.class);
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
Program program = method.getProgram();
|
||||
if (program != null) {
|
||||
transformProgram(program, context.getHierarchy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void transformProgram(Program program, ClassHierarchy hierarchy) {
|
||||
if (!hasForNameCall(program)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisjointSet varSet = new DisjointSet();
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
varSet.create();
|
||||
}
|
||||
int[] nameIndexes = new int[program.variableCount()];
|
||||
String[] constants = new String[program.variableCount()];
|
||||
Arrays.fill(nameIndexes, -1);
|
||||
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
for (Instruction instruction : block) {
|
||||
if (instruction instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
if (invoke.getMethod().equals(getNameMethod)) {
|
||||
if (invoke.getReceiver() != null) {
|
||||
nameIndexes[invoke.getReceiver().getIndex()] = invoke.getInstance().getIndex();
|
||||
}
|
||||
}
|
||||
} else if (instruction instanceof StringConstantInstruction) {
|
||||
StringConstantInstruction stringConstant = (StringConstantInstruction) instruction;
|
||||
constants[stringConstant.getReceiver().getIndex()] = stringConstant.getConstant();
|
||||
} else if (instruction instanceof AssignInstruction) {
|
||||
AssignInstruction assign = (AssignInstruction) instruction;
|
||||
varSet.union(assign.getAssignee().getIndex(), assign.getReceiver().getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nameIndexes = Arrays.copyOf(nameIndexes, varSet.size());
|
||||
int[] nameRepresentatives = new int[nameIndexes.length];
|
||||
Arrays.fill(nameRepresentatives, -1);
|
||||
String[] constantsByClasses = new String[varSet.size()];
|
||||
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
int varClass = varSet.find(i);
|
||||
if (nameRepresentatives[varClass] < 0) {
|
||||
nameRepresentatives[varClass] = i;
|
||||
}
|
||||
if (nameIndexes[i] >= 0) {
|
||||
nameIndexes[varClass] = varSet.find(nameIndexes[i]);
|
||||
}
|
||||
|
||||
constantsByClasses[varClass] = constants[i];
|
||||
}
|
||||
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
for (Instruction instruction : block) {
|
||||
if (!(instruction instanceof InvokeInstruction)) {
|
||||
continue;
|
||||
}
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
|
||||
if (!invoke.getMethod().equals(forNameMethod) && !invoke.getMethod().equals(forNameShortMethod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variable representative;
|
||||
|
||||
int classNameIndex = invoke.getArguments().get(0).getIndex();
|
||||
int nameIndex = nameIndexes[classNameIndex];
|
||||
String constant = constantsByClasses[invoke.getArguments().get(0).getIndex()];
|
||||
if (nameIndex >= 0) {
|
||||
representative = program.variableAt(nameRepresentatives[nameIndex]);
|
||||
} else if (constant != null) {
|
||||
if (hierarchy.getClassSource().get(constant) == null || !filterClassName(constant)) {
|
||||
InvokeInstruction invokeException = new InvokeInstruction();
|
||||
invokeException.setType(InvocationType.SPECIAL);
|
||||
invokeException.setMethod(new MethodReference(ExceptionHelpers.class, "classNotFound",
|
||||
Class.class));
|
||||
invokeException.setReceiver(program.createVariable());
|
||||
invokeException.setLocation(invoke.getLocation());
|
||||
invoke.insertPrevious(invokeException);
|
||||
representative = invokeException.getReceiver();
|
||||
} else {
|
||||
ClassConstantInstruction classConstant = new ClassConstantInstruction();
|
||||
classConstant.setConstant(ValueType.object(constant));
|
||||
classConstant.setReceiver(program.createVariable());
|
||||
classConstant.setLocation(invoke.getLocation());
|
||||
invoke.insertPrevious(classConstant);
|
||||
representative = classConstant.getReceiver();
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
InvokeInstruction initInvoke = new InvokeInstruction();
|
||||
initInvoke.setLocation(invoke.getLocation());
|
||||
initInvoke.setType(InvocationType.SPECIAL);
|
||||
initInvoke.setMethod(initMethod);
|
||||
initInvoke.setInstance(representative);
|
||||
invoke.insertPrevious(initInvoke);
|
||||
|
||||
if (invoke.getReceiver() == null) {
|
||||
invoke.delete();
|
||||
} else {
|
||||
AssignInstruction assign = new AssignInstruction();
|
||||
assign.setLocation(invoke.getLocation());
|
||||
assign.setAssignee(representative);
|
||||
assign.setReceiver(invoke.getReceiver());
|
||||
invoke.replace(assign);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasForNameCall(Program program) {
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
for (Instruction instruction : block) {
|
||||
if (!(instruction instanceof InvokeInstruction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
|
||||
if (invoke.getMethod().equals(forNameMethod) || invoke.getMethod().equals(forNameShortMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean filterClassName(String className) {
|
||||
switch (className) {
|
||||
// It's a hack for Kotlin. Kotlin full reflection library is too heavyweight for TeaVM.
|
||||
// This optimization enables full reflection when there's Kotlin/JVM reflection library
|
||||
// in the classpath, since Kotlin uses Class.forName() to check whether there's
|
||||
// full reflection library presents. If program does not use full reflection,
|
||||
// but build configuration includes kotlin-reflect artifact as a dependency,
|
||||
// it gets into classpath, which allows this optimization to be applied.
|
||||
case "kotlin.reflect.jvm.internal.ReflectionFactoryImpl":
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.teavm.classlib.impl.currency.CurrenciesGenerator;
|
|||
import org.teavm.classlib.impl.currency.CurrencyHelper;
|
||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||
import org.teavm.classlib.impl.record.ObjectMethodsSubstitutor;
|
||||
import org.teavm.classlib.impl.reflection.ReflectionTransformer;
|
||||
import org.teavm.classlib.impl.tz.DateTimeZoneProvider;
|
||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderPatch;
|
||||
|
@ -94,7 +95,7 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
if (!isBootstrap()) {
|
||||
host.registerService(CLDRReader.class, CLDRReader.getInstance(host.getProperties(), host.getClassLoader()));
|
||||
|
||||
host.add(new ClassForNameTransformer());
|
||||
host.add(new ReflectionTransformer());
|
||||
}
|
||||
|
||||
host.add(new AnnotationDependencyListener());
|
||||
|
|
|
@ -43,8 +43,9 @@ import org.teavm.model.ValueType;
|
|||
|
||||
public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||
private List<ReflectionSupplier> reflectionSuppliers;
|
||||
private MethodReference fieldGet = new MethodReference(Field.class, "get", Object.class, Object.class);
|
||||
private MethodReference fieldSet = new MethodReference(Field.class, "set", Object.class, Object.class, void.class);
|
||||
private MethodReference fieldGet = new MethodReference(Field.class, "getWithoutCheck", Object.class, Object.class);
|
||||
private MethodReference fieldSet = new MethodReference(Field.class, "setWithoutCheck", Object.class, Object.class,
|
||||
void.class);
|
||||
private MethodReference newInstance = new MethodReference(Constructor.class, "newInstance", Object[].class,
|
||||
Object.class);
|
||||
private MethodReference invokeMethod = new MethodReference(Method.class, "invoke", Object.class, Object[].class,
|
||||
|
|
|
@ -0,0 +1,524 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.impl.reflection;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import org.teavm.common.DisjointSet;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.ConstructInstruction;
|
||||
import org.teavm.model.instructions.GetFieldInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.RaiseInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
|
||||
public class ReflectionTransformer implements ClassHolderTransformer {
|
||||
private static final MethodReference getNameMethod = new MethodReference(Class.class, "getName", String.class);
|
||||
private static final MethodReference forNameMethod = new MethodReference(Class.class, "forName", String.class,
|
||||
boolean.class, ClassLoader.class, Class.class);
|
||||
private static final MethodReference forNameShortMethod = new MethodReference(Class.class, "forName",
|
||||
String.class, Class.class);
|
||||
private static final MethodReference newRefUpdaterMethod = new MethodReference(AtomicReferenceFieldUpdater.class,
|
||||
"newUpdater", Class.class, Class.class, String.class, AtomicReferenceFieldUpdater.class);
|
||||
private static final MethodReference newIntUpdaterMethod = new MethodReference(AtomicIntegerFieldUpdater.class,
|
||||
"newUpdater", Class.class, String.class, AtomicIntegerFieldUpdater.class);
|
||||
private static final MethodReference newLongUpdaterMethod = new MethodReference(AtomicLongFieldUpdater.class,
|
||||
"newUpdater", Class.class, String.class, AtomicLongFieldUpdater.class);
|
||||
private static final MethodReference initMethod = new MethodReference(Class.class, "initialize", void.class);
|
||||
|
||||
private Map<String, String> updaterClasses = new HashMap<>();
|
||||
|
||||
private boolean prepared;
|
||||
private DisjointSet varSet;
|
||||
private int[] nameRepresentatives;
|
||||
private String[] stringConstantsByClasses;
|
||||
private ValueType[] classConstantsByClasses;
|
||||
private boolean hasTruncatedBlocks;
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
Program program = method.getProgram();
|
||||
if (program != null) {
|
||||
transformProgram(program, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void transformProgram(Program program, ClassHolderTransformerContext context) {
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
for (Instruction instruction : block) {
|
||||
if (!(instruction instanceof InvokeInstruction)) {
|
||||
continue;
|
||||
}
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
|
||||
var method = invoke.getMethod();
|
||||
if (method.equals(forNameMethod) || method.equals(forNameShortMethod)) {
|
||||
transformForName(program, invoke, context);
|
||||
} else if (method.equals(newRefUpdaterMethod)) {
|
||||
transformRefUpdater(program, invoke, context);
|
||||
} else if (method.equals(newIntUpdaterMethod)) {
|
||||
transformPrimitiveUpdater(program, invoke,
|
||||
"java.util.concurrent.atomic.BaseAtomicIntegerFieldUpdater", ValueType.INTEGER, context);
|
||||
} else if (method.equals(newLongUpdaterMethod)) {
|
||||
transformPrimitiveUpdater(program, invoke,
|
||||
"java.util.concurrent.atomic.BaseAtomicLongFieldUpdater", ValueType.LONG, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasTruncatedBlocks) {
|
||||
new UnreachableBasicBlockEliminator().optimize(program);
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private void prepare(Program program) {
|
||||
if (prepared) {
|
||||
return;
|
||||
}
|
||||
prepared = true;
|
||||
|
||||
varSet = new DisjointSet();
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
varSet.create();
|
||||
}
|
||||
var nameIndexes = new int[program.variableCount()];
|
||||
var stringConstants = new String[program.variableCount()];
|
||||
var classConstants = new ValueType[program.variableCount()];
|
||||
Arrays.fill(nameIndexes, -1);
|
||||
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
for (Instruction instruction : block) {
|
||||
if (instruction instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
if (invoke.getMethod().equals(getNameMethod)) {
|
||||
if (invoke.getReceiver() != null) {
|
||||
nameIndexes[invoke.getReceiver().getIndex()] = invoke.getInstance().getIndex();
|
||||
}
|
||||
}
|
||||
} else if (instruction instanceof StringConstantInstruction) {
|
||||
StringConstantInstruction stringConstant = (StringConstantInstruction) instruction;
|
||||
stringConstants[stringConstant.getReceiver().getIndex()] = stringConstant.getConstant();
|
||||
} else if (instruction instanceof ClassConstantInstruction) {
|
||||
var classConstant = (ClassConstantInstruction) instruction;
|
||||
classConstants[classConstant.getReceiver().getIndex()] = classConstant.getConstant();
|
||||
} else if (instruction instanceof AssignInstruction) {
|
||||
AssignInstruction assign = (AssignInstruction) instruction;
|
||||
varSet.union(assign.getAssignee().getIndex(), assign.getReceiver().getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nameRepresentatives = new int[varSet.size()];
|
||||
Arrays.fill(nameRepresentatives, -1);
|
||||
stringConstantsByClasses = new String[varSet.size()];
|
||||
classConstantsByClasses = new ValueType[varSet.size()];
|
||||
|
||||
for (int i = 0; i < program.variableCount(); i++) {
|
||||
int varClass = varSet.find(i);
|
||||
if (nameIndexes[i] >= 0) {
|
||||
nameRepresentatives[varClass] = varSet.find(nameIndexes[i]);
|
||||
}
|
||||
|
||||
stringConstantsByClasses[varClass] = stringConstants[i];
|
||||
classConstantsByClasses[varClass] = classConstants[i];
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
if (prepared) {
|
||||
prepared = false;
|
||||
hasTruncatedBlocks = false;
|
||||
varSet = null;
|
||||
nameRepresentatives = null;
|
||||
stringConstantsByClasses = null;
|
||||
classConstantsByClasses = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void transformForName(Program program, InvokeInstruction invoke, ClassHolderTransformerContext context) {
|
||||
var hierarchy = context.getHierarchy();
|
||||
prepare(program);
|
||||
|
||||
Variable representative;
|
||||
|
||||
int classNameIndex = varSet.find(invoke.getArguments().get(0).getIndex());
|
||||
var nameIndex = nameRepresentatives[classNameIndex];
|
||||
String constant = stringConstantsByClasses[classNameIndex];
|
||||
if (nameIndex >= 0) {
|
||||
representative = program.variableAt(nameIndex);
|
||||
} else if (constant != null) {
|
||||
if (hierarchy.getClassSource().get(constant) == null || !filterClassName(constant)) {
|
||||
emitException(invoke, ClassNotFoundException.class);
|
||||
return;
|
||||
} else {
|
||||
ClassConstantInstruction classConstant = new ClassConstantInstruction();
|
||||
classConstant.setConstant(ValueType.object(constant));
|
||||
classConstant.setReceiver(program.createVariable());
|
||||
classConstant.setLocation(invoke.getLocation());
|
||||
invoke.insertPrevious(classConstant);
|
||||
representative = classConstant.getReceiver();
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeInstruction initInvoke = new InvokeInstruction();
|
||||
initInvoke.setLocation(invoke.getLocation());
|
||||
initInvoke.setType(InvocationType.SPECIAL);
|
||||
initInvoke.setMethod(initMethod);
|
||||
initInvoke.setInstance(representative);
|
||||
invoke.insertPrevious(initInvoke);
|
||||
|
||||
if (invoke.getReceiver() == null) {
|
||||
invoke.delete();
|
||||
} else {
|
||||
AssignInstruction assign = new AssignInstruction();
|
||||
assign.setLocation(invoke.getLocation());
|
||||
assign.setAssignee(representative);
|
||||
assign.setReceiver(invoke.getReceiver());
|
||||
invoke.replace(assign);
|
||||
}
|
||||
}
|
||||
|
||||
private void transformRefUpdater(Program program, InvokeInstruction invoke,
|
||||
ClassHolderTransformerContext context) {
|
||||
prepare(program);
|
||||
|
||||
var targetTypeConstant = classConstantsByClasses[varSet.find(invoke.getArguments().get(0).getIndex())];
|
||||
var varTypeConstant = classConstantsByClasses[varSet.find(invoke.getArguments().get(1).getIndex())];
|
||||
var nameConstant = stringConstantsByClasses[varSet.find(invoke.getArguments().get(2).getIndex())];
|
||||
|
||||
if (targetTypeConstant == null || varTypeConstant == null || nameConstant == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(targetTypeConstant instanceof ValueType.Object)) {
|
||||
emitException(invoke, IllegalArgumentException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
var className = ((ValueType.Object) targetTypeConstant).getClassName();
|
||||
var cls = context.getHierarchy().getClassSource().get(className);
|
||||
if (cls == null) {
|
||||
emitException(invoke, NoClassDefFoundError.class);
|
||||
return;
|
||||
}
|
||||
|
||||
var field = cls.getField(nameConstant);
|
||||
if (field == null) {
|
||||
emitException(invoke, RuntimeException.class, NoSuchFieldException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!field.getType().equals(varTypeConstant)) {
|
||||
emitException(invoke, ClassCastException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!field.hasModifier(ElementModifier.VOLATILE) || varTypeConstant instanceof ValueType.Primitive
|
||||
|| varTypeConstant == ValueType.VOID || field.hasModifier(ElementModifier.STATIC)) {
|
||||
emitException(invoke, IllegalArgumentException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
var updaterClassName = getRefUpdaterClass(context, field);
|
||||
var getField = new GetFieldInstruction();
|
||||
getField.setField(new FieldReference(updaterClassName, "INSTANCE"));
|
||||
getField.setFieldType(ValueType.object(updaterClassName));
|
||||
getField.setLocation(invoke.getLocation());
|
||||
getField.setReceiver(invoke.getReceiver());
|
||||
invoke.replace(getField);
|
||||
}
|
||||
|
||||
private String getRefUpdaterClass(ClassHolderTransformerContext context, FieldReader field) {
|
||||
var key = field.getReference().toString();
|
||||
return updaterClasses.computeIfAbsent(key, k -> createRefUpdaterClass(context, field));
|
||||
}
|
||||
|
||||
private String createRefUpdaterClass(ClassHolderTransformerContext context, FieldReader field) {
|
||||
var className = field.getOwnerName() + "$" + field.getName() + "$_AtomicUpdater$";
|
||||
var updaterClass = new ClassHolder(className);
|
||||
updaterClass.setLevel(AccessLevel.PUBLIC);
|
||||
updaterClass.setParent("java.util.concurrent.atomic.BaseAtomicReferenceFieldUpdater");
|
||||
fillClass(updaterClass, field.getOwnerName(), context.getHierarchy());
|
||||
updaterClass.addMethod(createGetRefMethod(field, className, context.getHierarchy()));
|
||||
updaterClass.addMethod(createSetRefMethod(field, className, context.getHierarchy()));
|
||||
context.submit(updaterClass);
|
||||
return className;
|
||||
}
|
||||
|
||||
private MethodHolder createGetRefMethod(FieldReader field, String className, ClassHierarchy hierarchy) {
|
||||
var method = new MethodHolder("get", ValueType.object("java.lang.Object"),
|
||||
ValueType.object("java.lang.Object"));
|
||||
method.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
var pe = ProgramEmitter.create(method, hierarchy);
|
||||
var instance = pe.var(1, Object.class);
|
||||
pe.invoke(className, "check", ValueType.object(field.getOwnerName()), instance)
|
||||
.getField(field.getName(), field.getType())
|
||||
.returnValue();
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private MethodHolder createSetRefMethod(FieldReader field, String className, ClassHierarchy hierarchy) {
|
||||
var method = new MethodHolder("set", ValueType.object("java.lang.Object"),
|
||||
ValueType.object("java.lang.Object"), ValueType.VOID);
|
||||
method.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
var pe = ProgramEmitter.create(method, hierarchy);
|
||||
var instance = pe.var(1, Object.class);
|
||||
var value = pe.var(2, Object.class);
|
||||
pe.invoke(className, "check", ValueType.object(field.getOwnerName()), instance)
|
||||
.setField(field.getName(), value.cast(field.getType()));
|
||||
pe.exit();
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private void transformPrimitiveUpdater(Program program, InvokeInstruction invoke, String superclass,
|
||||
ValueType primitiveType, ClassHolderTransformerContext context) {
|
||||
prepare(program);
|
||||
|
||||
var targetTypeConstant = classConstantsByClasses[varSet.find(invoke.getArguments().get(0).getIndex())];
|
||||
var nameConstant = stringConstantsByClasses[varSet.find(invoke.getArguments().get(1).getIndex())];
|
||||
|
||||
if (targetTypeConstant == null || nameConstant == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(targetTypeConstant instanceof ValueType.Object)) {
|
||||
emitException(invoke, IllegalArgumentException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
var className = ((ValueType.Object) targetTypeConstant).getClassName();
|
||||
var cls = context.getHierarchy().getClassSource().get(className);
|
||||
if (cls == null) {
|
||||
emitException(invoke, NoClassDefFoundError.class);
|
||||
return;
|
||||
}
|
||||
|
||||
var field = cls.getField(nameConstant);
|
||||
if (field == null) {
|
||||
emitException(invoke, RuntimeException.class, NoSuchFieldException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!field.hasModifier(ElementModifier.VOLATILE) || field.hasModifier(ElementModifier.STATIC)
|
||||
|| !field.getType().equals(primitiveType)) {
|
||||
emitException(invoke, IllegalArgumentException.class);
|
||||
return;
|
||||
}
|
||||
|
||||
var updaterClassName = getPrimitiveUpdaterClass(context, field, superclass);
|
||||
var getField = new GetFieldInstruction();
|
||||
getField.setField(new FieldReference(updaterClassName, "INSTANCE"));
|
||||
getField.setFieldType(ValueType.object(updaterClassName));
|
||||
getField.setLocation(invoke.getLocation());
|
||||
getField.setReceiver(invoke.getReceiver());
|
||||
invoke.replace(getField);
|
||||
}
|
||||
|
||||
private String getPrimitiveUpdaterClass(ClassHolderTransformerContext context, FieldReader field,
|
||||
String superclass) {
|
||||
var key = field.getReference().toString();
|
||||
return updaterClasses.computeIfAbsent(key, k -> createPrimitiveUpdaterClass(context, field, superclass));
|
||||
}
|
||||
|
||||
private String createPrimitiveUpdaterClass(ClassHolderTransformerContext context, FieldReader field,
|
||||
String superclass) {
|
||||
var className = field.getOwnerName() + "$" + field.getName() + "$_AtomicUpdater$";
|
||||
var updaterClass = new ClassHolder(className);
|
||||
updaterClass.setLevel(AccessLevel.PUBLIC);
|
||||
updaterClass.setParent(superclass);
|
||||
fillClass(updaterClass, field.getOwnerName(), context.getHierarchy());
|
||||
updaterClass.addMethod(createGetPrimitiveMethod(field, className, context.getHierarchy()));
|
||||
updaterClass.addMethod(createSetPrimitiveMethod(field, className, context.getHierarchy()));
|
||||
context.submit(updaterClass);
|
||||
return className;
|
||||
}
|
||||
|
||||
private MethodHolder createGetPrimitiveMethod(FieldReader field, String className, ClassHierarchy hierarchy) {
|
||||
var method = new MethodHolder("get", ValueType.object("java.lang.Object"), field.getType());
|
||||
method.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
var pe = ProgramEmitter.create(method, hierarchy);
|
||||
var instance = pe.var(1, Object.class);
|
||||
pe.invoke(className, "check", ValueType.object(field.getOwnerName()), instance)
|
||||
.getField(field.getName(), field.getType())
|
||||
.returnValue();
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private MethodHolder createSetPrimitiveMethod(FieldReader field, String className, ClassHierarchy hierarchy) {
|
||||
var method = new MethodHolder("set", ValueType.object("java.lang.Object"), field.getType(), ValueType.VOID);
|
||||
method.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
var pe = ProgramEmitter.create(method, hierarchy);
|
||||
var instance = pe.var(1, Object.class);
|
||||
var value = pe.var(2, field.getType());
|
||||
pe.invoke(className, "check", ValueType.object(field.getOwnerName()), instance)
|
||||
.setField(field.getName(), value);
|
||||
pe.exit();
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private void fillClass(ClassHolder cls, String targetClassName, ClassHierarchy hierarchy) {
|
||||
var instanceField = new FieldHolder("INSTANCE");
|
||||
instanceField.setType(ValueType.object(cls.getName()));
|
||||
instanceField.setLevel(AccessLevel.PUBLIC);
|
||||
instanceField.getModifiers().add(ElementModifier.STATIC);
|
||||
cls.addField(instanceField);
|
||||
|
||||
cls.addMethod(createConstructor(cls, hierarchy));
|
||||
cls.addMethod(createInitializer(cls, hierarchy));
|
||||
cls.addMethod(createCheck(targetClassName, hierarchy));
|
||||
}
|
||||
|
||||
private MethodHolder createConstructor(ClassHolder cls, ClassHierarchy hierarchy) {
|
||||
var ctor = new MethodHolder("<init>", ValueType.VOID);
|
||||
ctor.setLevel(AccessLevel.PRIVATE);
|
||||
|
||||
var pe = ProgramEmitter.create(ctor, hierarchy);
|
||||
pe.var(0, AtomicReferenceFieldUpdater.class).invokeSpecial(cls.getParent(), "<init>", ValueType.VOID);
|
||||
pe.exit();
|
||||
|
||||
return ctor;
|
||||
}
|
||||
|
||||
private MethodHolder createInitializer(ClassHolder cls, ClassHierarchy hierarchy) {
|
||||
var initializer = new MethodHolder("<clinit>", ValueType.VOID);
|
||||
initializer.setLevel(AccessLevel.PRIVATE);
|
||||
initializer.getModifiers().add(ElementModifier.STATIC);
|
||||
|
||||
var pe = ProgramEmitter.create(initializer, hierarchy);
|
||||
pe.setField(cls.getName(), "INSTANCE", pe.construct(cls.getName()));
|
||||
pe.exit();
|
||||
|
||||
return initializer;
|
||||
}
|
||||
|
||||
private MethodHolder createCheck(String targetClassName, ClassHierarchy hierarchy) {
|
||||
var method = new MethodHolder("check", ValueType.object("java.lang.Object"),
|
||||
ValueType.object(targetClassName));
|
||||
method.setLevel(AccessLevel.PRIVATE);
|
||||
method.getModifiers().add(ElementModifier.STATIC);
|
||||
|
||||
var pe = ProgramEmitter.create(method, hierarchy);
|
||||
var instance = pe.var(1, ValueType.object("java.lang.Object"));
|
||||
pe.when(instance.isNull()).thenDo(() -> {
|
||||
pe.construct(ClassCastException.class).raise();
|
||||
});
|
||||
instance.cast(ValueType.object(targetClassName)).returnValue();
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private void emitException(Instruction instruction, Class<?> exceptionType) {
|
||||
emitException(instruction, exceptionType, null);
|
||||
}
|
||||
|
||||
private void emitException(Instruction instruction, Class<?> exceptionType, Class<?> wrappedExceptionType) {
|
||||
hasTruncatedBlocks = true;
|
||||
ProgramUtils.truncateBlock(instruction);
|
||||
|
||||
var program = instruction.getProgram();
|
||||
var block = instruction.getBasicBlock();
|
||||
|
||||
var construct = new ConstructInstruction();
|
||||
construct.setType(exceptionType.getName());
|
||||
construct.setReceiver(program.createVariable());
|
||||
construct.setLocation(instruction.getLocation());
|
||||
block.add(construct);
|
||||
|
||||
var init = new InvokeInstruction();
|
||||
init.setType(InvocationType.SPECIAL);
|
||||
init.setInstance(construct.getReceiver());
|
||||
|
||||
if (wrappedExceptionType != null) {
|
||||
var wrappedConstruct = new ConstructInstruction();
|
||||
wrappedConstruct.setType(wrappedExceptionType.getName());
|
||||
wrappedConstruct.setReceiver(program.createVariable());
|
||||
wrappedConstruct.setLocation(instruction.getLocation());
|
||||
block.add(wrappedConstruct);
|
||||
|
||||
var wrappedInit = new InvokeInstruction();
|
||||
wrappedInit.setType(InvocationType.SPECIAL);
|
||||
wrappedInit.setInstance(wrappedConstruct.getReceiver());
|
||||
wrappedInit.setMethod(new MethodReference(wrappedExceptionType, "<init>", void.class));
|
||||
wrappedInit.setLocation(instruction.getLocation());
|
||||
block.add(wrappedInit);
|
||||
|
||||
init.setMethod(new MethodReference(exceptionType, "<init>", Throwable.class, void.class));
|
||||
init.setArguments(wrappedConstruct.getReceiver());
|
||||
} else {
|
||||
init.setMethod(new MethodReference(exceptionType, "<init>", void.class));
|
||||
}
|
||||
init.setLocation(instruction.getLocation());
|
||||
block.add(init);
|
||||
|
||||
var raise = new RaiseInstruction();
|
||||
raise.setException(construct.getReceiver());
|
||||
raise.setLocation(instruction.getLocation());
|
||||
block.add(raise);
|
||||
|
||||
instruction.delete();
|
||||
}
|
||||
|
||||
private boolean filterClassName(String className) {
|
||||
switch (className) {
|
||||
// It's a hack for Kotlin. Kotlin full reflection library is too heavyweight for TeaVM.
|
||||
// This optimization enables full reflection when there's Kotlin/JVM reflection library
|
||||
// in the classpath, since Kotlin uses Class.forName() to check whether there's
|
||||
// full reflection library presents. If program does not use full reflection,
|
||||
// but build configuration includes kotlin-reflect artifact as a dependency,
|
||||
// it gets into classpath, which allows this optimization to be applied.
|
||||
case "kotlin.reflect.jvm.internal.ReflectionFactoryImpl":
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -354,19 +354,19 @@ public class TClass<T> extends TObject implements TAnnotatedElement, TType {
|
|||
return fields.clone();
|
||||
}
|
||||
|
||||
public TField getDeclaredField(String name) throws TNoSuchFieldError {
|
||||
public TField getDeclaredField(String name) throws TNoSuchFieldException {
|
||||
for (TField field : getDeclaredFields()) {
|
||||
if (field.getName().equals(name)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
throw new TNoSuchFieldError();
|
||||
throw new TNoSuchFieldException();
|
||||
}
|
||||
|
||||
public TField getField(String name) throws TNoSuchFieldError {
|
||||
public TField getField(String name) throws TNoSuchFieldException {
|
||||
TField result = findField(name, new HashSet<>());
|
||||
if (result == null) {
|
||||
throw new TNoSuchFieldError();
|
||||
throw new TNoSuchFieldException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2014 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.classlib.java.lang;
|
||||
|
||||
public class TNoSuchFieldException extends TReflectiveOperationException {
|
||||
public TNoSuchFieldException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TNoSuchFieldException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -86,19 +86,23 @@ public class TField extends TAccessibleObject implements TMember {
|
|||
}
|
||||
|
||||
public Object get(Object obj) throws TIllegalArgumentException, TIllegalAccessException {
|
||||
if (getter == null) {
|
||||
throw new TIllegalAccessException();
|
||||
}
|
||||
checkGetAccess();
|
||||
checkInstance(obj);
|
||||
return getWithoutCheck(obj);
|
||||
}
|
||||
|
||||
public Object getWithoutCheck(Object obj) {
|
||||
PlatformObject result = getter.get(Platform.getPlatformObject(obj));
|
||||
return Converter.toJava(result);
|
||||
}
|
||||
|
||||
public void set(Object obj, Object value) throws TIllegalArgumentException, TIllegalAccessException {
|
||||
if (setter == null) {
|
||||
throw new TIllegalAccessException();
|
||||
}
|
||||
checkSetAccess();
|
||||
checkInstance(obj);
|
||||
setWithoutCheck(obj, value);
|
||||
}
|
||||
|
||||
public void setWithoutCheck(Object obj, Object value) {
|
||||
setter.set(Platform.getPlatformObject(obj), Converter.fromJava(value));
|
||||
}
|
||||
|
||||
|
@ -112,4 +116,16 @@ public class TField extends TAccessibleObject implements TMember {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkGetAccess() throws TIllegalAccessException {
|
||||
if (getter == null) {
|
||||
throw new TIllegalAccessException();
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSetAccess() throws TIllegalAccessException {
|
||||
if (setter == null) {
|
||||
throw new TIllegalAccessException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import org.teavm.classlib.java.lang.TClass;
|
||||
import org.teavm.classlib.java.lang.TIllegalAccessException;
|
||||
import org.teavm.classlib.java.lang.TNoSuchFieldException;
|
||||
|
||||
public abstract class TAtomicIntegerFieldUpdater<T> {
|
||||
protected TAtomicIntegerFieldUpdater() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("EqualsBetweenInconvertibleTypes")
|
||||
public static <U> TAtomicIntegerFieldUpdater<U> newUpdater(TClass<U> tclass, String fieldName) {
|
||||
try {
|
||||
var field = tclass.getDeclaredField(fieldName);
|
||||
if (!Modifier.isVolatile(field.getModifiers()) || Modifier.isStatic(field.getModifiers())
|
||||
|| !field.getType().equals(int.class)) {
|
||||
throw new IllegalArgumentException();
|
||||
} else {
|
||||
field.checkGetAccess();
|
||||
field.checkSetAccess();
|
||||
return new TReflectionBasedAtomicIntegerFieldUpdater<>(field);
|
||||
}
|
||||
} catch (TNoSuchFieldException | TIllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean compareAndSet(T obj, int expect, int update);
|
||||
|
||||
public abstract boolean weakCompareAndSet(T obj, int expect, int update);
|
||||
|
||||
public abstract void set(T obj, int newValue);
|
||||
|
||||
public abstract void lazySet(T obj, int newValue);
|
||||
|
||||
public abstract int get(T obj);
|
||||
|
||||
public int getAndSet(T obj, int newValue) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getAndIncrement(T obj) {
|
||||
return getAndAdd(obj, 1);
|
||||
}
|
||||
|
||||
public int getAndDecrement(T obj) {
|
||||
return getAndAdd(obj, -1);
|
||||
}
|
||||
|
||||
public int getAndAdd(T obj, int delta) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, currentValue + delta)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int incrementAndGet(T obj) {
|
||||
return addAndGet(obj, 1);
|
||||
}
|
||||
|
||||
public int decrementAndGet(T obj) {
|
||||
return addAndGet(obj, -1);
|
||||
}
|
||||
|
||||
public int addAndGet(T obj, int delta) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, currentValue + delta)) {
|
||||
return currentValue + delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final int getAndUpdate(T obj, IntUnaryOperator updateFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = updateFunction.applyAsInt(currentValue);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final int updateAndGet(T obj, IntUnaryOperator updateFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = updateFunction.applyAsInt(currentValue);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final int getAndAccumulate(T obj, int x, IntBinaryOperator accumulatorFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = accumulatorFunction.applyAsInt(currentValue, x);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final int accumulateAndGet(T obj, int x, IntBinaryOperator accumulatorFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = accumulatorFunction.applyAsInt(currentValue, x);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
import org.teavm.classlib.java.lang.TClass;
|
||||
import org.teavm.classlib.java.lang.TIllegalAccessException;
|
||||
import org.teavm.classlib.java.lang.TNoSuchFieldException;
|
||||
|
||||
public abstract class TAtomicLongFieldUpdater<T> {
|
||||
protected TAtomicLongFieldUpdater() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("EqualsBetweenInconvertibleTypes")
|
||||
public static <U> TAtomicLongFieldUpdater<U> newUpdater(TClass<U> tclass, String fieldName) {
|
||||
try {
|
||||
var field = tclass.getDeclaredField(fieldName);
|
||||
if (!Modifier.isVolatile(field.getModifiers()) || Modifier.isStatic(field.getModifiers())
|
||||
|| !field.getType().equals(long.class)) {
|
||||
throw new IllegalArgumentException();
|
||||
} else {
|
||||
field.checkGetAccess();
|
||||
field.checkSetAccess();
|
||||
return new TReflectionBasedAtomicLongFieldUpdater<>(field);
|
||||
}
|
||||
} catch (TNoSuchFieldException | TIllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean compareAndSet(T obj, long expect, long update);
|
||||
|
||||
public abstract boolean weakCompareAndSet(T obj, long expect, long update);
|
||||
|
||||
public abstract void set(T obj, long newValue);
|
||||
|
||||
public abstract void lazySet(T obj, long newValue);
|
||||
|
||||
public abstract long get(T obj);
|
||||
|
||||
public long getAndSet(T obj, long newValue) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getAndIncrement(T obj) {
|
||||
return getAndAdd(obj, 1);
|
||||
}
|
||||
|
||||
public long getAndDecrement(T obj) {
|
||||
return getAndAdd(obj, -1);
|
||||
}
|
||||
|
||||
public long getAndAdd(T obj, long delta) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, currentValue + delta)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long incrementAndGet(T obj) {
|
||||
return addAndGet(obj, 1);
|
||||
}
|
||||
|
||||
public long decrementAndGet(T obj) {
|
||||
return addAndGet(obj, -1);
|
||||
}
|
||||
|
||||
public long addAndGet(T obj, long delta) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, currentValue + delta)) {
|
||||
return currentValue + delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final long getAndUpdate(T obj, LongUnaryOperator updateFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = updateFunction.applyAsLong(currentValue);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final long updateAndGet(T obj, LongUnaryOperator updateFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = updateFunction.applyAsLong(currentValue);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final long getAndAccumulate(T obj, long x, LongBinaryOperator accumulatorFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = accumulatorFunction.applyAsLong(currentValue, x);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final long accumulateAndGet(T obj, long x, LongBinaryOperator accumulatorFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = accumulatorFunction.applyAsLong(currentValue, x);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.UnaryOperator;
|
||||
import org.teavm.classlib.java.lang.TClass;
|
||||
import org.teavm.classlib.java.lang.TIllegalAccessException;
|
||||
import org.teavm.classlib.java.lang.TNoSuchFieldException;
|
||||
|
||||
public abstract class TAtomicReferenceFieldUpdater<T, V> {
|
||||
protected TAtomicReferenceFieldUpdater() {
|
||||
}
|
||||
|
||||
public static <U, W> TAtomicReferenceFieldUpdater<U, W> newUpdater(TClass<U> tclass, TClass<W> vclass,
|
||||
String fieldName) {
|
||||
try {
|
||||
var field = tclass.getDeclaredField(fieldName);
|
||||
if (field.getType() != vclass) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
if (!Modifier.isVolatile(field.getModifiers()) || Modifier.isStatic(field.getModifiers())
|
||||
|| field.getType().isPrimitive()) {
|
||||
throw new IllegalArgumentException();
|
||||
} else {
|
||||
field.checkGetAccess();
|
||||
field.checkSetAccess();
|
||||
return new TReflectionBasedAtomicReferenceFieldUpdater<>(field);
|
||||
}
|
||||
} catch (TNoSuchFieldException | TIllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean compareAndSet(T obj, V expect, V update);
|
||||
|
||||
public abstract boolean weakCompareAndSet(T obj, V expect, V update);
|
||||
|
||||
public abstract void set(T obj, V newValue);
|
||||
|
||||
public abstract void lazySet(T obj, V newValue);
|
||||
|
||||
public abstract V get(T obj);
|
||||
|
||||
public V getAndSet(T obj, V newValue) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final V getAndUpdate(T obj, UnaryOperator<V> updateFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = updateFunction.apply(currentValue);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final V updateAndGet(T obj, UnaryOperator<V> updateFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = updateFunction.apply(currentValue);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final V getAndAccumulate(T obj, V x, BinaryOperator<V> accumulatorFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = accumulatorFunction.apply(currentValue, x);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final V accumulateAndGet(T obj, V x, BinaryOperator<V> accumulatorFunction) {
|
||||
while (true) {
|
||||
var currentValue = get(obj);
|
||||
var newValue = accumulatorFunction.apply(currentValue, x);
|
||||
if (compareAndSet(obj, currentValue, newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
abstract class TBaseAtomicIntegerFieldUpdater<T> extends TAtomicIntegerFieldUpdater<T> {
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, int expect, int update) {
|
||||
if (get(obj) != expect) {
|
||||
return false;
|
||||
}
|
||||
set(obj, update);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean weakCompareAndSet(T obj, int expect, int update) {
|
||||
return compareAndSet(obj, expect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lazySet(T obj, int newValue) {
|
||||
set(obj, newValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
abstract class TBaseAtomicLongFieldUpdater<T> extends TAtomicLongFieldUpdater<T> {
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, long expect, long update) {
|
||||
if (get(obj) != expect) {
|
||||
return false;
|
||||
}
|
||||
set(obj, update);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean weakCompareAndSet(T obj, long expect, long update) {
|
||||
return compareAndSet(obj, expect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lazySet(T obj, long newValue) {
|
||||
set(obj, newValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
abstract class TBaseAtomicReferenceFieldUpdater<T, V> extends TAtomicReferenceFieldUpdater<T, V> {
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, V expect, V update) {
|
||||
if (get(obj) != expect) {
|
||||
return false;
|
||||
}
|
||||
set(obj, update);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean weakCompareAndSet(T obj, V expect, V update) {
|
||||
return compareAndSet(obj, expect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lazySet(T obj, V newValue) {
|
||||
set(obj, newValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
abstract class TLongOptimizedAtomicReferenceFieldUpdater<T>
|
||||
extends TBaseAtomicReferenceFieldUpdater<T, Long> {
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, Long expect, Long update) {
|
||||
if (getAsLong(obj) != expect) {
|
||||
return false;
|
||||
}
|
||||
set(obj, update.longValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, Long newValue) {
|
||||
set(obj, newValue.longValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long get(T obj) {
|
||||
return getAsLong(obj);
|
||||
}
|
||||
|
||||
abstract long getAsLong(T obj);
|
||||
|
||||
abstract void set(T obj, long value);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import org.teavm.classlib.java.lang.TObject;
|
||||
import org.teavm.classlib.java.lang.reflect.TField;
|
||||
|
||||
class TReflectionBasedAtomicIntegerFieldUpdater<T> extends TAtomicIntegerFieldUpdater<T> {
|
||||
private TField field;
|
||||
|
||||
TReflectionBasedAtomicIntegerFieldUpdater(TField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, int expect, int update) {
|
||||
checkInstance(obj);
|
||||
if (((Integer) field.getWithoutCheck(obj)) != expect) {
|
||||
return false;
|
||||
}
|
||||
field.setWithoutCheck(obj, update);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean weakCompareAndSet(T obj, int expect, int update) {
|
||||
return compareAndSet(obj, expect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, int newValue) {
|
||||
checkInstance(obj);
|
||||
field.setWithoutCheck(obj, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lazySet(T obj, int newValue) {
|
||||
set(obj, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get(T obj) {
|
||||
checkInstance(obj);
|
||||
return (Integer) field.getWithoutCheck(obj);
|
||||
}
|
||||
|
||||
private void checkInstance(T obj) {
|
||||
if (!field.getDeclaringClass().isInstance((TObject) obj)) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import org.teavm.classlib.java.lang.TObject;
|
||||
import org.teavm.classlib.java.lang.reflect.TField;
|
||||
|
||||
class TReflectionBasedAtomicLongFieldUpdater<T> extends TAtomicLongFieldUpdater<T> {
|
||||
private TField field;
|
||||
|
||||
TReflectionBasedAtomicLongFieldUpdater(TField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, long expect, long update) {
|
||||
checkInstance(obj);
|
||||
if (((Long) field.getWithoutCheck(obj)) != expect) {
|
||||
return false;
|
||||
}
|
||||
field.setWithoutCheck(obj, update);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean weakCompareAndSet(T obj, long expect, long update) {
|
||||
return compareAndSet(obj, expect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, long newValue) {
|
||||
checkInstance(obj);
|
||||
field.setWithoutCheck(obj, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lazySet(T obj, long newValue) {
|
||||
set(obj, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long get(T obj) {
|
||||
checkInstance(obj);
|
||||
return (Long) field.getWithoutCheck(obj);
|
||||
}
|
||||
|
||||
private void checkInstance(T obj) {
|
||||
if (!field.getDeclaringClass().isInstance((TObject) obj)) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import org.teavm.classlib.java.lang.TObject;
|
||||
import org.teavm.classlib.java.lang.reflect.TField;
|
||||
|
||||
class TReflectionBasedAtomicReferenceFieldUpdater<T, V> extends TAtomicReferenceFieldUpdater<T, V> {
|
||||
private TField field;
|
||||
|
||||
TReflectionBasedAtomicReferenceFieldUpdater(TField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compareAndSet(T obj, V expect, V update) {
|
||||
checkInstance(obj);
|
||||
if (field.getWithoutCheck(obj) != expect) {
|
||||
return false;
|
||||
}
|
||||
field.setWithoutCheck(obj, update);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean weakCompareAndSet(T obj, V expect, V update) {
|
||||
return compareAndSet(obj, expect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, V newValue) {
|
||||
checkInstance(obj);
|
||||
field.setWithoutCheck(obj, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lazySet(T obj, V newValue) {
|
||||
set(obj, newValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public V get(T obj) {
|
||||
checkInstance(obj);
|
||||
return (V) field.getWithoutCheck(obj);
|
||||
}
|
||||
|
||||
private void checkInstance(T obj) {
|
||||
if (!field.getDeclaringClass().isInstance((TObject) obj)) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
|
@ -27,8 +26,38 @@ import org.teavm.dependency.MethodDependencyInfo;
|
|||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.SupportedOn;
|
||||
import org.teavm.interop.UnsupportedOn;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
import org.teavm.model.AnnotationContainerReader;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||
import org.teavm.model.instructions.ConstructInstruction;
|
||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||
import org.teavm.model.instructions.GetFieldInstruction;
|
||||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
import org.teavm.model.instructions.InstructionVisitor;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||
import org.teavm.model.instructions.PutFieldInstruction;
|
||||
import org.teavm.model.instructions.RaiseInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||
|
||||
public class MissingItemsProcessor {
|
||||
|
@ -98,45 +127,7 @@ public class MissingItemsProcessor {
|
|||
}
|
||||
|
||||
private void truncateBlock(Instruction instruction) {
|
||||
var transitionExtractor = new TransitionExtractor();
|
||||
var block = instruction.getBasicBlock();
|
||||
if (block.getLastInstruction() != null) {
|
||||
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
||||
}
|
||||
for (var successor : transitionExtractor.getTargets()) {
|
||||
successor.removeIncomingsFrom(block);
|
||||
}
|
||||
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
var handlers = new LinkedHashSet<BasicBlock>();
|
||||
for (var tryCatch : block.getTryCatchBlocks()) {
|
||||
handlers.add(tryCatch.getHandler());
|
||||
}
|
||||
|
||||
var next = instruction;
|
||||
var assignExtractor = new AssignmentExtractor();
|
||||
while (next != null) {
|
||||
next.acceptVisitor(assignExtractor);
|
||||
var definition = assignExtractor.getResult();
|
||||
if (definition != null) {
|
||||
for (var handler : handlers) {
|
||||
for (var phi : handler.getPhis()) {
|
||||
for (var iter = phi.getIncomings().iterator(); iter.hasNext();) {
|
||||
var incoming = iter.next();
|
||||
if (incoming.getSource() == block && incoming.getValue() == definition) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next = next.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
while (instruction.getNext() != null) {
|
||||
instruction.getNext().delete();
|
||||
}
|
||||
ProgramUtils.truncateBlock(instruction);
|
||||
instruction.insertNextAll(instructionsToAdd);
|
||||
instruction.delete();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.model.util;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.ControlFlowEntry;
|
||||
|
@ -250,4 +251,46 @@ public final class ProgramUtils {
|
|||
}
|
||||
return varsDefinedInBlock;
|
||||
}
|
||||
|
||||
public static void truncateBlock(Instruction instruction) {
|
||||
var transitionExtractor = new TransitionExtractor();
|
||||
var block = instruction.getBasicBlock();
|
||||
if (block.getLastInstruction() != null) {
|
||||
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
||||
}
|
||||
for (var successor : transitionExtractor.getTargets()) {
|
||||
successor.removeIncomingsFrom(block);
|
||||
}
|
||||
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
var handlers = new LinkedHashSet<BasicBlock>();
|
||||
for (var tryCatch : block.getTryCatchBlocks()) {
|
||||
handlers.add(tryCatch.getHandler());
|
||||
}
|
||||
|
||||
var next = instruction;
|
||||
var assignExtractor = new AssignmentExtractor();
|
||||
while (next != null) {
|
||||
next.acceptVisitor(assignExtractor);
|
||||
var definition = assignExtractor.getResult();
|
||||
if (definition != null) {
|
||||
for (var handler : handlers) {
|
||||
for (var phi : handler.getPhis()) {
|
||||
for (var iter = phi.getIncomings().iterator(); iter.hasNext();) {
|
||||
var incoming = iter.next();
|
||||
if (incoming.getSource() == block && incoming.getValue() == definition) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next = next.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
while (instruction.getNext() != null) {
|
||||
instruction.getNext().delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
|
@ -121,6 +122,20 @@ public class ClassTest {
|
|||
assertEquals(1, ((TestObject) instance).getCounter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceCreatedThoughReflectionWithConstantName() throws Exception {
|
||||
var cls = Class.forName("org.teavm.classlib.java.lang.ClassTest$ClassReferredByConstantName");
|
||||
assertArrayEquals(new Class<?>[] { Supplier.class }, cls.getInterfaces());
|
||||
assertEquals(Object.class, cls.getSuperclass());
|
||||
}
|
||||
|
||||
private static class ClassReferredByConstantName implements Supplier<String> {
|
||||
@Override
|
||||
public String get() {
|
||||
return "constantNameWorks";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceCreatedThroughReflectionAsync() throws Exception {
|
||||
Runnable instance = TestObjectAsync.class.newInstance();
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.classlib.support.Reflectable;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
|
||||
public class AtomicIntegerFieldUpdaterReflectionTest {
|
||||
private Class<ClassWithField> getInstanceType() {
|
||||
return ClassWithField.class;
|
||||
}
|
||||
@Test
|
||||
public void getSet() {
|
||||
var updater = AtomicIntegerFieldUpdater.newUpdater(getInstanceType(), "value");
|
||||
var obj = new ClassWithField();
|
||||
obj.value = 1;
|
||||
|
||||
assertEquals(1, updater.get(obj));
|
||||
updater.set(obj, 2);
|
||||
assertEquals(2, obj.value);
|
||||
|
||||
assertEquals(2, updater.getAndSet(obj, 3));
|
||||
assertEquals(3, obj.value);
|
||||
|
||||
assertFalse(updater.compareAndSet(obj, 2, 4));
|
||||
assertEquals(3, obj.value);
|
||||
assertTrue(updater.compareAndSet(obj, 3, 4));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndIncrement(obj));
|
||||
assertEquals(5, obj.value);
|
||||
|
||||
assertEquals(5, updater.getAndDecrement(obj));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndAdd(obj, 2));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(7, updater.incrementAndGet(obj));
|
||||
assertEquals(7, obj.value);
|
||||
|
||||
assertEquals(6, updater.decrementAndGet(obj));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(8, updater.addAndGet(obj, 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndUpdate(obj, v -> v * 2));
|
||||
assertEquals(16, obj.value);
|
||||
|
||||
assertEquals(8, updater.updateAndGet(obj, v -> v / 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndAccumulate(obj, 3, (x, y) -> x * y));
|
||||
assertEquals(24, obj.value);
|
||||
|
||||
assertEquals(48, updater.accumulateAndGet(obj, 2, (x, y) -> x * y));
|
||||
assertEquals(48, obj.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonVolatileField() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(getInstanceType(), "nonVolatileValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refField() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(getInstanceType(), "refValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticField() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(getInstanceType(), "staticValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFieldName() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(getInstanceType(), "foo");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(NoSuchFieldException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void invalidClass() {
|
||||
var updater = (AtomicIntegerFieldUpdater<?>) AtomicIntegerFieldUpdater.newUpdater(
|
||||
getInstanceType(), "value");
|
||||
var objUpdater = (AtomicIntegerFieldUpdater<Object>) updater;
|
||||
try {
|
||||
objUpdater.set(new Object(), 1);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
objUpdater.set(null, 2);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithField {
|
||||
@Reflectable
|
||||
volatile int value;
|
||||
|
||||
@Reflectable
|
||||
int nonVolatileValue;
|
||||
|
||||
@Reflectable
|
||||
volatile Object refValue;
|
||||
|
||||
@Reflectable
|
||||
static volatile int staticValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class AtomicIntegerFieldUpdaterTest {
|
||||
@Test
|
||||
public void getSet() {
|
||||
var updater = AtomicIntegerFieldUpdater.newUpdater(ClassWithField.class, "value");
|
||||
var obj = new ClassWithField();
|
||||
obj.value = 1;
|
||||
|
||||
assertEquals(1, updater.get(obj));
|
||||
updater.set(obj, 2);
|
||||
assertEquals(2, obj.value);
|
||||
|
||||
assertEquals(2, updater.getAndSet(obj, 3));
|
||||
assertEquals(3, obj.value);
|
||||
|
||||
assertFalse(updater.compareAndSet(obj, 2, 4));
|
||||
assertEquals(3, obj.value);
|
||||
assertTrue(updater.compareAndSet(obj, 3, 4));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndIncrement(obj));
|
||||
assertEquals(5, obj.value);
|
||||
|
||||
assertEquals(5, updater.getAndDecrement(obj));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndAdd(obj, 2));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(7, updater.incrementAndGet(obj));
|
||||
assertEquals(7, obj.value);
|
||||
|
||||
assertEquals(6, updater.decrementAndGet(obj));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(8, updater.addAndGet(obj, 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndUpdate(obj, v -> v * 2));
|
||||
assertEquals(16, obj.value);
|
||||
|
||||
assertEquals(8, updater.updateAndGet(obj, v -> v / 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndAccumulate(obj, 3, (x, y) -> x * y));
|
||||
assertEquals(24, obj.value);
|
||||
|
||||
assertEquals(48, updater.accumulateAndGet(obj, 2, (x, y) -> x * y));
|
||||
assertEquals(48, obj.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonVolatileField() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(ClassWithField.class, "nonVolatileValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refField() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(ClassWithField.class, "refValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticField() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(ClassWithField.class, "staticValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFieldName() {
|
||||
try {
|
||||
AtomicIntegerFieldUpdater.newUpdater(ClassWithField.class, "foo");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(NoSuchFieldException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void invalidClass() {
|
||||
var updater = (AtomicIntegerFieldUpdater<?>) AtomicIntegerFieldUpdater.newUpdater(
|
||||
ClassWithField.class, "value");
|
||||
var objUpdater = (AtomicIntegerFieldUpdater<Object>) updater;
|
||||
try {
|
||||
objUpdater.set(new Object(), 1);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
objUpdater.set(null, 2);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithField {
|
||||
volatile int value;
|
||||
int nonVolatileValue;
|
||||
volatile Object refValue;
|
||||
static volatile int staticValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.classlib.support.Reflectable;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
|
||||
public class AtomicLongFieldUpdaterReflectionTest {
|
||||
private Class<ClassWithField> getInstanceType() {
|
||||
return ClassWithField.class;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSet() {
|
||||
var updater = AtomicLongFieldUpdater.newUpdater(getInstanceType(), "value");
|
||||
var obj = new ClassWithField();
|
||||
obj.value = 1;
|
||||
|
||||
assertEquals(1, updater.get(obj));
|
||||
updater.set(obj, 2);
|
||||
assertEquals(2, obj.value);
|
||||
|
||||
assertEquals(2, updater.getAndSet(obj, 3));
|
||||
assertEquals(3, obj.value);
|
||||
|
||||
assertFalse(updater.compareAndSet(obj, 2, 4));
|
||||
assertEquals(3, obj.value);
|
||||
assertTrue(updater.compareAndSet(obj, 3, 4));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndIncrement(obj));
|
||||
assertEquals(5, obj.value);
|
||||
|
||||
assertEquals(5, updater.getAndDecrement(obj));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndAdd(obj, 2));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(7, updater.incrementAndGet(obj));
|
||||
assertEquals(7, obj.value);
|
||||
|
||||
assertEquals(6, updater.decrementAndGet(obj));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(8, updater.addAndGet(obj, 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndUpdate(obj, v -> v * 2));
|
||||
assertEquals(16, obj.value);
|
||||
|
||||
assertEquals(8, updater.updateAndGet(obj, v -> v / 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndAccumulate(obj, 3, (x, y) -> x * y));
|
||||
assertEquals(24, obj.value);
|
||||
|
||||
assertEquals(48, updater.accumulateAndGet(obj, 2, (x, y) -> x * y));
|
||||
assertEquals(48, obj.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonVolatileField() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(getInstanceType(), "nonVolatileValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refField() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(getInstanceType(), "refValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticField() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(getInstanceType(), "staticValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFieldName() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(getInstanceType(), "foo");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(NoSuchFieldException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void invalidClass() {
|
||||
var updater = (AtomicLongFieldUpdater<?>) AtomicLongFieldUpdater.newUpdater(getInstanceType(), "value");
|
||||
var objUpdater = (AtomicLongFieldUpdater<Object>) updater;
|
||||
try {
|
||||
objUpdater.set(new Object(), 1);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
objUpdater.set(null, 2);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithField {
|
||||
@Reflectable
|
||||
volatile long value;
|
||||
|
||||
@Reflectable
|
||||
long nonVolatileValue;
|
||||
|
||||
@Reflectable
|
||||
volatile Object refValue;
|
||||
|
||||
@Reflectable
|
||||
static volatile long staticValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class AtomicLongFieldUpdaterTest {
|
||||
@Test
|
||||
public void getSet() {
|
||||
var updater = AtomicLongFieldUpdater.newUpdater(ClassWithField.class, "value");
|
||||
var obj = new ClassWithField();
|
||||
obj.value = 1;
|
||||
|
||||
assertEquals(1, updater.get(obj));
|
||||
updater.set(obj, 2);
|
||||
assertEquals(2, obj.value);
|
||||
|
||||
assertEquals(2, updater.getAndSet(obj, 3));
|
||||
assertEquals(3, obj.value);
|
||||
|
||||
assertFalse(updater.compareAndSet(obj, 2, 4));
|
||||
assertEquals(3, obj.value);
|
||||
assertTrue(updater.compareAndSet(obj, 3, 4));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndIncrement(obj));
|
||||
assertEquals(5, obj.value);
|
||||
|
||||
assertEquals(5, updater.getAndDecrement(obj));
|
||||
assertEquals(4, obj.value);
|
||||
|
||||
assertEquals(4, updater.getAndAdd(obj, 2));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(7, updater.incrementAndGet(obj));
|
||||
assertEquals(7, obj.value);
|
||||
|
||||
assertEquals(6, updater.decrementAndGet(obj));
|
||||
assertEquals(6, obj.value);
|
||||
|
||||
assertEquals(8, updater.addAndGet(obj, 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndUpdate(obj, v -> v * 2));
|
||||
assertEquals(16, obj.value);
|
||||
|
||||
assertEquals(8, updater.updateAndGet(obj, v -> v / 2));
|
||||
assertEquals(8, obj.value);
|
||||
|
||||
assertEquals(8, updater.getAndAccumulate(obj, 3, (x, y) -> x * y));
|
||||
assertEquals(24, obj.value);
|
||||
|
||||
assertEquals(48, updater.accumulateAndGet(obj, 2, (x, y) -> x * y));
|
||||
assertEquals(48, obj.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonVolatileField() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(ClassWithField.class, "nonVolatileValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refField() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(ClassWithField.class, "refValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticField() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(ClassWithField.class, "staticValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFieldName() {
|
||||
try {
|
||||
AtomicLongFieldUpdater.newUpdater(ClassWithField.class, "foo");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(NoSuchFieldException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void invalidClass() {
|
||||
var updater = (AtomicLongFieldUpdater<?>) AtomicLongFieldUpdater.newUpdater(ClassWithField.class, "value");
|
||||
var objUpdater = (AtomicLongFieldUpdater<Object>) updater;
|
||||
try {
|
||||
objUpdater.set(new Object(), 1);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
objUpdater.set(null, 2);
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithField {
|
||||
volatile long value;
|
||||
long nonVolatileValue;
|
||||
volatile Object refValue;
|
||||
static volatile long staticValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.classlib.support.Reflectable;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
|
||||
public class AtomicReferenceFieldUpdaterReflectionTest {
|
||||
private Class<ClassWithField> getInstanceType() {
|
||||
return ClassWithField.class;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSet() {
|
||||
var updater = AtomicReferenceFieldUpdater.newUpdater(getInstanceType(), ValueClass.class, "value");
|
||||
var obj = new ClassWithField();
|
||||
var a = new ValueClass("a");
|
||||
var b = new ValueClass("b");
|
||||
var c = new ValueClass("c");
|
||||
obj.value = a;
|
||||
|
||||
assertSame(a, updater.get(obj));
|
||||
updater.set(obj, b);
|
||||
assertSame(b, obj.value);
|
||||
|
||||
assertSame(b, updater.getAndSet(obj, a));
|
||||
assertSame(a, obj.value);
|
||||
|
||||
assertFalse(updater.compareAndSet(obj, b, c));
|
||||
assertSame(a, obj.value);
|
||||
assertTrue(updater.compareAndSet(obj, a, c));
|
||||
assertSame(c, obj.value);
|
||||
|
||||
assertSame(c, updater.getAndUpdate(obj, v -> new ValueClass(v.v + "1")));
|
||||
assertEquals("c1", obj.value.v);
|
||||
|
||||
assertEquals("c11", updater.updateAndGet(obj, v -> new ValueClass(v.v + "1")).v);
|
||||
assertEquals("c11", obj.value.v);
|
||||
|
||||
assertEquals("c11", updater.getAndAccumulate(obj, b, (x, y) -> new ValueClass(x.v + "," + y.v)).v);
|
||||
assertEquals("c11,b", obj.value.v);
|
||||
|
||||
assertEquals("c11,b,a", updater.accumulateAndGet(obj, a, (x, y) -> new ValueClass(x.v + "," + y.v)).v);
|
||||
assertEquals("c11,b,a", obj.value.v);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongType() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(getInstanceType(), String.class, "value");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonVolatileField() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(getInstanceType(), ValueClass.class, "nonVolatileValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void primitiveField() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(getInstanceType(), int.class, "intValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticField() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(getInstanceType(), ValueClass.class, "staticValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFieldName() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(getInstanceType(), ValueClass.class, "foo");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(NoSuchFieldException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void invalidClass() {
|
||||
var updater = (AtomicReferenceFieldUpdater<?, ?>) AtomicReferenceFieldUpdater.newUpdater(
|
||||
getInstanceType(), ValueClass.class, "value");
|
||||
var objUpdater = (AtomicReferenceFieldUpdater<Object, ValueClass>) updater;
|
||||
try {
|
||||
objUpdater.set(new Object(), new ValueClass(""));
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
objUpdater.set(null, new ValueClass(""));
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithField {
|
||||
@Reflectable
|
||||
volatile ValueClass value;
|
||||
|
||||
@Reflectable
|
||||
ValueClass nonVolatileValue;
|
||||
|
||||
@Reflectable
|
||||
volatile int intValue;
|
||||
|
||||
@Reflectable
|
||||
static volatile ValueClass staticValue;
|
||||
}
|
||||
|
||||
private static class ValueClass {
|
||||
String v;
|
||||
|
||||
ValueClass(String v) {
|
||||
this.v = v;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class AtomicReferenceFieldUpdaterTest {
|
||||
@Test
|
||||
public void getSet() {
|
||||
var updater = AtomicReferenceFieldUpdater.newUpdater(ClassWithField.class, ValueClass.class, "value");
|
||||
var obj = new ClassWithField();
|
||||
var a = new ValueClass("a");
|
||||
var b = new ValueClass("b");
|
||||
var c = new ValueClass("c");
|
||||
obj.value = a;
|
||||
|
||||
assertSame(a, updater.get(obj));
|
||||
updater.set(obj, b);
|
||||
assertSame(b, obj.value);
|
||||
|
||||
assertSame(b, updater.getAndSet(obj, a));
|
||||
assertSame(a, obj.value);
|
||||
|
||||
assertFalse(updater.compareAndSet(obj, b, c));
|
||||
assertSame(a, obj.value);
|
||||
assertTrue(updater.compareAndSet(obj, a, c));
|
||||
assertSame(c, obj.value);
|
||||
|
||||
assertSame(c, updater.getAndUpdate(obj, v -> new ValueClass(v.v + "1")));
|
||||
assertEquals("c1", obj.value.v);
|
||||
|
||||
assertEquals("c11", updater.updateAndGet(obj, v -> new ValueClass(v.v + "1")).v);
|
||||
assertEquals("c11", obj.value.v);
|
||||
|
||||
assertEquals("c11", updater.getAndAccumulate(obj, b, (x, y) -> new ValueClass(x.v + "," + y.v)).v);
|
||||
assertEquals("c11,b", obj.value.v);
|
||||
|
||||
assertEquals("c11,b,a", updater.accumulateAndGet(obj, a, (x, y) -> new ValueClass(x.v + "," + y.v)).v);
|
||||
assertEquals("c11,b,a", obj.value.v);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongType() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(ClassWithField.class, String.class, "value");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonVolatileField() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(ClassWithField.class, ValueClass.class, "nonVolatileValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void primitiveField() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(ClassWithField.class, int.class, "intValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticField() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(ClassWithField.class, ValueClass.class, "staticValue");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFieldName() {
|
||||
try {
|
||||
AtomicReferenceFieldUpdater.newUpdater(ClassWithField.class, ValueClass.class, "foo");
|
||||
fail("Expected exception not thrown");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals(NoSuchFieldException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void invalidClass() {
|
||||
var updater = (AtomicReferenceFieldUpdater<?, ?>) AtomicReferenceFieldUpdater.newUpdater(
|
||||
ClassWithField.class, ValueClass.class, "value");
|
||||
var objUpdater = (AtomicReferenceFieldUpdater<Object, ValueClass>) updater;
|
||||
try {
|
||||
objUpdater.set(new Object(), new ValueClass(""));
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
objUpdater.set(null, new ValueClass(""));
|
||||
fail("Expected exception not thrown");
|
||||
} catch (ClassCastException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithField {
|
||||
volatile ValueClass value;
|
||||
ValueClass nonVolatileValue;
|
||||
volatile int intValue;
|
||||
static volatile ValueClass staticValue;
|
||||
}
|
||||
|
||||
private static class ValueClass {
|
||||
String v;
|
||||
|
||||
ValueClass(String v) {
|
||||
this.v = v;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2023 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.classlib.java.util.concurrent.atomic;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.classlib.support.Reflectable;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
|
||||
public class MultiThreadedFieldUpdaterTest {
|
||||
@Test
|
||||
public void getAndUpdate() {
|
||||
var updater = AtomicReferenceFieldUpdater.newUpdater(TestClass.class, String.class, "value");
|
||||
var obj = new TestClass();
|
||||
obj.value = "foo";
|
||||
|
||||
var monitor1 = new Monitor();
|
||||
var monitor2 = new Monitor();
|
||||
var thread = new Thread(() -> {
|
||||
synchronized (monitor1) {
|
||||
if (monitor1.count > 0) {
|
||||
try {
|
||||
monitor1.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
updater.set(obj, "bar");
|
||||
synchronized (monitor2) {
|
||||
monitor2.count = 0;
|
||||
monitor2.notifyAll();
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
updater.getAndUpdate(obj, value -> {
|
||||
synchronized (monitor1) {
|
||||
monitor1.count = 0;
|
||||
monitor1.notifyAll();
|
||||
}
|
||||
var result = value + "!";
|
||||
synchronized (monitor2) {
|
||||
if (monitor2.count > 0) {
|
||||
try {
|
||||
monitor2.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
assertEquals("bar!", obj.value);
|
||||
}
|
||||
|
||||
private static class TestClass {
|
||||
@Reflectable
|
||||
volatile String value;
|
||||
}
|
||||
|
||||
private static class Monitor {
|
||||
int count = 1;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user