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.currency.CurrencyHelper;
|
||||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||||
import org.teavm.classlib.impl.record.ObjectMethodsSubstitutor;
|
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.DateTimeZoneProvider;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderPatch;
|
import org.teavm.classlib.impl.tz.DateTimeZoneProviderPatch;
|
||||||
|
@ -94,7 +95,7 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
if (!isBootstrap()) {
|
if (!isBootstrap()) {
|
||||||
host.registerService(CLDRReader.class, CLDRReader.getInstance(host.getProperties(), host.getClassLoader()));
|
host.registerService(CLDRReader.class, CLDRReader.getInstance(host.getProperties(), host.getClassLoader()));
|
||||||
|
|
||||||
host.add(new ClassForNameTransformer());
|
host.add(new ReflectionTransformer());
|
||||||
}
|
}
|
||||||
|
|
||||||
host.add(new AnnotationDependencyListener());
|
host.add(new AnnotationDependencyListener());
|
||||||
|
|
|
@ -43,8 +43,9 @@ import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class ReflectionDependencyListener extends AbstractDependencyListener {
|
public class ReflectionDependencyListener extends AbstractDependencyListener {
|
||||||
private List<ReflectionSupplier> reflectionSuppliers;
|
private List<ReflectionSupplier> reflectionSuppliers;
|
||||||
private MethodReference fieldGet = new MethodReference(Field.class, "get", Object.class, Object.class);
|
private MethodReference fieldGet = new MethodReference(Field.class, "getWithoutCheck", Object.class, Object.class);
|
||||||
private MethodReference fieldSet = new MethodReference(Field.class, "set", Object.class, Object.class, void.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,
|
private MethodReference newInstance = new MethodReference(Constructor.class, "newInstance", Object[].class,
|
||||||
Object.class);
|
Object.class);
|
||||||
private MethodReference invokeMethod = new MethodReference(Method.class, "invoke", 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();
|
return fields.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TField getDeclaredField(String name) throws TNoSuchFieldError {
|
public TField getDeclaredField(String name) throws TNoSuchFieldException {
|
||||||
for (TField field : getDeclaredFields()) {
|
for (TField field : getDeclaredFields()) {
|
||||||
if (field.getName().equals(name)) {
|
if (field.getName().equals(name)) {
|
||||||
return field;
|
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<>());
|
TField result = findField(name, new HashSet<>());
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new TNoSuchFieldError();
|
throw new TNoSuchFieldException();
|
||||||
}
|
}
|
||||||
return result;
|
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 {
|
public Object get(Object obj) throws TIllegalArgumentException, TIllegalAccessException {
|
||||||
if (getter == null) {
|
checkGetAccess();
|
||||||
throw new TIllegalAccessException();
|
|
||||||
}
|
|
||||||
checkInstance(obj);
|
checkInstance(obj);
|
||||||
|
return getWithoutCheck(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getWithoutCheck(Object obj) {
|
||||||
PlatformObject result = getter.get(Platform.getPlatformObject(obj));
|
PlatformObject result = getter.get(Platform.getPlatformObject(obj));
|
||||||
return Converter.toJava(result);
|
return Converter.toJava(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(Object obj, Object value) throws TIllegalArgumentException, TIllegalAccessException {
|
public void set(Object obj, Object value) throws TIllegalArgumentException, TIllegalAccessException {
|
||||||
if (setter == null) {
|
checkSetAccess();
|
||||||
throw new TIllegalAccessException();
|
|
||||||
}
|
|
||||||
checkInstance(obj);
|
checkInstance(obj);
|
||||||
|
setWithoutCheck(obj, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWithoutCheck(Object obj, Object value) {
|
||||||
setter.set(Platform.getPlatformObject(obj), Converter.fromJava(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.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
@ -27,8 +26,38 @@ import org.teavm.dependency.MethodDependencyInfo;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.interop.SupportedOn;
|
import org.teavm.interop.SupportedOn;
|
||||||
import org.teavm.interop.UnsupportedOn;
|
import org.teavm.interop.UnsupportedOn;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.AnnotationContainerReader;
|
||||||
import org.teavm.model.instructions.*;
|
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;
|
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||||
|
|
||||||
public class MissingItemsProcessor {
|
public class MissingItemsProcessor {
|
||||||
|
@ -98,45 +127,7 @@ public class MissingItemsProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void truncateBlock(Instruction instruction) {
|
private void truncateBlock(Instruction instruction) {
|
||||||
var transitionExtractor = new TransitionExtractor();
|
ProgramUtils.truncateBlock(instruction);
|
||||||
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();
|
|
||||||
}
|
|
||||||
instruction.insertNextAll(instructionsToAdd);
|
instruction.insertNextAll(instructionsToAdd);
|
||||||
instruction.delete();
|
instruction.delete();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.model.util;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.ast.ControlFlowEntry;
|
import org.teavm.ast.ControlFlowEntry;
|
||||||
|
@ -250,4 +251,46 @@ public final class ProgramUtils {
|
||||||
}
|
}
|
||||||
return varsDefinedInBlock;
|
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.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.teavm.junit.SkipPlatform;
|
import org.teavm.junit.SkipPlatform;
|
||||||
|
@ -121,6 +122,20 @@ public class ClassTest {
|
||||||
assertEquals(1, ((TestObject) instance).getCounter());
|
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
|
@Test
|
||||||
public void instanceCreatedThroughReflectionAsync() throws Exception {
|
public void instanceCreatedThroughReflectionAsync() throws Exception {
|
||||||
Runnable instance = TestObjectAsync.class.newInstance();
|
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