Simple metaprogramming test passes

This commit is contained in:
Alexey Andreev 2016-02-27 18:46:27 +03:00
parent 4819eee3ef
commit 3e562aa08a
20 changed files with 197 additions and 125 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Eclipse" />
<option name="DEFAULT_COMPILER" value="Javac" />
<excludeFromCompile>
<directory url="file://$PROJECT_DIR$/tools/maven/webapp/src/main/resources/archetype-resources" includeSubdirectories="true" />
</excludeFromCompile>

View File

@ -17,7 +17,6 @@ package org.teavm.dependency;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@ -55,10 +54,6 @@ import org.teavm.model.util.ProgramUtils;
import org.teavm.optimization.UnreachableBasicBlockEliminator;
import org.teavm.parsing.Parser;
/**
*
* @author Alexey Andreev
*/
public class DependencyChecker implements DependencyInfo {
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
private int classNameSuffix;
@ -73,14 +68,13 @@ public class DependencyChecker implements DependencyInfo {
private ServiceRepository services;
private Queue<Runnable> tasks = new ArrayDeque<>();
List<DependencyType> types = new ArrayList<>();
Map<String, DependencyType> typeMap = new HashMap<>();
private Map<String, DependencyType> typeMap = new HashMap<>();
private DependencyCheckerInterruptor interruptor;
private boolean interrupted;
private Diagnostics diagnostics;
DefaultCallGraph callGraph = new DefaultCallGraph();
private DependencyAgent agent;
List<DependencyNode> nodes = new ArrayList<>();
List<BitSet> typeBitSets = new ArrayList<>();
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
private boolean completing;
@ -133,14 +127,13 @@ public class DependencyChecker implements DependencyInfo {
if (type == null) {
type = new DependencyType(this, name, types.size());
types.add(type);
typeBitSets.add(new BitSet(nodes.size()));
typeMap.put(name, type);
}
return type;
}
public DependencyNode createNode() {
DependencyNode node = new DependencyNode(this, nodes.size());
DependencyNode node = new DependencyNode(this);
nodes.add(node);
return node;
}

View File

@ -18,10 +18,6 @@ package org.teavm.dependency;
import java.util.*;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class DependencyNode implements ValueDependencyInfo {
private DependencyChecker dependencyChecker;
private List<DependencyConsumer> followers;
@ -32,17 +28,15 @@ public class DependencyNode implements ValueDependencyInfo {
private DependencyNode arrayItemNode;
private DependencyNode classValueNode;
private int degree;
int index;
boolean locked;
MethodReference method;
DependencyNode(DependencyChecker dependencyChecker, int index) {
this(dependencyChecker, index, 0);
DependencyNode(DependencyChecker dependencyChecker) {
this(dependencyChecker, 0);
}
DependencyNode(DependencyChecker dependencyChecker, int index, int degree) {
private DependencyNode(DependencyChecker dependencyChecker, int degree) {
this.dependencyChecker = dependencyChecker;
this.index = index;
this.degree = degree;
}
@ -192,7 +186,7 @@ public class DependencyNode implements ValueDependencyInfo {
@Override
public DependencyNode getArrayItem() {
if (arrayItemNode == null) {
arrayItemNode = new DependencyNode(dependencyChecker, dependencyChecker.nodes.size(), degree + 1);
arrayItemNode = new DependencyNode(dependencyChecker, degree + 1);
dependencyChecker.nodes.add(arrayItemNode);
if (DependencyChecker.shouldLog) {
arrayItemNode.tag = tag + "[";
@ -204,7 +198,7 @@ public class DependencyNode implements ValueDependencyInfo {
public DependencyNode getClassValueNode() {
if (classValueNode == null) {
classValueNode = new DependencyNode(dependencyChecker, dependencyChecker.nodes.size(), degree);
classValueNode = new DependencyNode(dependencyChecker, degree);
dependencyChecker.nodes.add(classValueNode);
if (DependencyChecker.shouldLog) {
classValueNode.tag = tag + "@";

View File

@ -15,12 +15,8 @@
*/
package org.teavm.dependency;
/**
*
* @author Alexey Andreev
*/
class DependencyNodeToNodeTransition implements DependencyConsumer {
DependencyNode source;
private DependencyNode source;
DependencyNode destination;
private DependencyTypeFilter filter;

View File

@ -20,10 +20,6 @@ import java.util.Collections;
import java.util.List;
import org.teavm.model.CallLocation;
/**
*
* @author Alexey Andreev
*/
public class AccumulationDiagnostics implements Diagnostics, ProblemProvider {
private List<Problem> problems = new ArrayList<>();
private List<Problem> readonlyProblems = Collections.unmodifiableList(problems);

View File

@ -17,10 +17,6 @@ package org.teavm.diagnostics;
import org.teavm.model.CallLocation;
/**
*
* @author Alexey Andreev
*/
public interface Diagnostics {
void error(CallLocation location, String error, Object... params);

View File

@ -24,6 +24,7 @@ import org.teavm.dependency.MethodDependency;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.metaprogramming.impl.model.MethodDescriber;
import org.teavm.metaprogramming.impl.model.MethodModel;
import org.teavm.metaprogramming.impl.reflect.ReflectContext;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
@ -40,6 +41,11 @@ public class MetaprogrammingDependencyListener extends AbstractDependencyListene
public void started(DependencyAgent agent) {
proxyClassLoader = new MetaprogrammingClassLoader(agent.getClassLoader());
describer = new MethodDescriber(agent.getDiagnostics(), agent.getClassSource());
MetaprogrammingImpl.classLoader = proxyClassLoader;
MetaprogrammingImpl.classSource = agent.getClassSource();
MetaprogrammingImpl.agent = agent;
MetaprogrammingImpl.reflectContext = new ReflectContext(agent.getClassSource(), proxyClassLoader);
}
@Override
@ -56,8 +62,9 @@ public class MetaprogrammingDependencyListener extends AbstractDependencyListene
ProgramEmitter pe = ProgramEmitter.create(model.getMethod().getDescriptor(), agent.getClassSource());
ValueEmitter[] paramVars = new ValueEmitter[model.getMetaParameterCount()];
int offset = model.isStatic() ? 1 : 0;
for (int i = 0; i < paramVars.length; ++i) {
paramVars[i] = pe.var(i, model.getMetaParameterType(i));
paramVars[i] = pe.var(i + offset, model.getMetaParameterType(i));
}
if (model.getUsages().size() == 1) {
@ -91,7 +98,7 @@ public class MetaprogrammingDependencyListener extends AbstractDependencyListene
ValueEmitter[] paramVars) {
MethodDependencyInfo methodDep = agent.getMethod(model.getMethod());
ValueEmitter paramVar = paramVars[model.getMetaClassParameterIndex()];
ValueEmitter tag = paramVar.invokeVirtual("getClass", Class.class).invokeVirtual("getName", String.class);
ValueEmitter tag = paramVar.invokeVirtual("getName", String.class);
StringChooseEmitter choice = pe.stringChoice(tag);
for (Map.Entry<ValueType, MethodReference> usageEntry : model.getUsages().entrySet()) {

View File

@ -53,6 +53,7 @@ public final class MetaprogrammingImpl {
private MetaprogrammingImpl() {
}
@SuppressWarnings("WeakerAccess")
public static <T> Value<T> emit(Computation<T> computation) {
if (computation instanceof ValueImpl<?>) {
@SuppressWarnings("unchecked")
@ -90,7 +91,8 @@ public final class MetaprogrammingImpl {
return new LazyValueImpl<>(varContext, computation, type, generator.forcedLocation);
}
public static void exit(Value<?> value) {
@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
public static void exit(Computation<?> value) {
if (value == null) {
returnValue(null);
return;
@ -103,19 +105,12 @@ public final class MetaprogrammingImpl {
generator.blockIndex = generator.returnBlockIndex;
returnValue(unbox(generator.getResultVar()));
} else if (value instanceof ValueImpl) {
ValueImpl<?> valueImpl = (ValueImpl<?>) value;
returnValue(unbox(varContext.emitVariable(valueImpl, new CallLocation(templateMethod,
generator.location))));
} else if (value instanceof LazyValueImpl) {
Variable var = generator.lazy((LazyValueImpl<?>) value);
returnValue(unbox(var));
} else {
throw new IllegalStateException("Unexpected computation type: " + value.getClass().getName());
}
}
static Variable unbox(Variable var) {
private static Variable unbox(Variable var) {
if (returnType instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) returnType).getKind()) {
case BOOLEAN:
@ -147,7 +142,7 @@ public final class MetaprogrammingImpl {
return var;
}
static Variable unbox(Variable var, Class<?> boxed, Class<?> primitive) {
private static Variable unbox(Variable var, Class<?> boxed, Class<?> primitive) {
InvokeInstruction insn = new InvokeInstruction();
insn.setInstance(var);
insn.setType(InvocationType.VIRTUAL);
@ -170,10 +165,12 @@ public final class MetaprogrammingImpl {
unsupported();
}
@SuppressWarnings("WeakerAccess")
public static ReflectClass<?> findClass(String name) {
return reflectContext.findClass(name);
}
@SuppressWarnings("WeakerAccess")
public static <T> ReflectClass<T> findClass(Class<T> cls) {
return reflectContext.findClass(cls);
}
@ -200,6 +197,7 @@ public final class MetaprogrammingImpl {
return proxy(findClass(type), handler);
}
@SuppressWarnings("WeakerAccess")
public static <T> Value<T> proxy(ReflectClass<T> type, InvocationHandler<T> handler) {
unsupported();
return null;

View File

@ -0,0 +1,37 @@
/*
* Copyright 2016 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.metaprogramming.impl;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.CallLocation;
import org.teavm.model.Variable;
class TopLevelVariableContext extends VariableContext {
private Diagnostics diagnostics;
TopLevelVariableContext(Diagnostics diagnostics) {
super(null);
this.diagnostics = diagnostics;
}
@Override
public Variable emitVariable(ValueImpl<?> value, CallLocation location) {
if (value.context != this) {
diagnostics.error(location, "Can't get variable from another context");
}
return value.innerValue;
}
}

View File

@ -23,7 +23,6 @@ import java.lang.reflect.Method;
import java.util.Map;
import java.util.WeakHashMap;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyType;
import org.teavm.dependency.MethodDependency;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.metaprogramming.CompileTime;
@ -41,16 +40,17 @@ import org.teavm.model.Variable;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
public class UsageGenerator {
class UsageGenerator {
private static Map<DependencyAgent, Integer> suffixGenerator = new WeakHashMap<>();
DependencyAgent agent;
MethodModel model;
MethodDependency methodDep;
CallLocation location;
Diagnostics diagnostics;
Method proxyMethod;
private DependencyAgent agent;
private MethodModel model;
private MethodDependency methodDep;
private CallLocation location;
private Diagnostics diagnostics;
private Method proxyMethod;
private MetaprogrammingClassLoader classLoader;
private boolean annotationErrorReported;
private MethodDependency nameDependency;
UsageGenerator(DependencyAgent agent, MethodModel model, MethodDependency methodDep, CallLocation location,
MetaprogrammingClassLoader classLoader) {
@ -65,10 +65,6 @@ public class UsageGenerator {
public void installProxyEmitter() {
Diagnostics diagnostics = agent.getDiagnostics();
MethodDependency getClassDep = agent.linkMethod(new MethodReference(Object.class, "getClass", Class.class),
location);
getClassDep.getThrown().connect(methodDep.getThrown());
try {
proxyMethod = getJavaMethod(classLoader, model.getMetaMethod());
proxyMethod.setAccessible(true);
@ -80,20 +76,20 @@ public class UsageGenerator {
return;
}
nameDependency = installAdditionalDependencies();
if (model.getClassParameterIndex() >= 0) {
int index = (model.isStatic() ? 0 : 1) + model.getClassParameterIndex();
methodDep.getVariable(index).addConsumer(type -> consumeType(type, getClassDep));
int index = 1 + model.getClassParameterIndex();
methodDep.getVariable(index).getClassValueNode().addConsumer(
type -> emitPermutation(findClass(type.getName())));
} else {
emitPermutation(null, getClassDep);
emitPermutation(null);
}
}
installAdditionalDependencies(getClassDep);
}
private void installAdditionalDependencies(MethodDependency getClassDep) {
private MethodDependency installAdditionalDependencies() {
MethodDependency nameDep = agent.linkMethod(new MethodReference(Class.class, "getName", String.class),
location);
getClassDep.getResult().connect(nameDep.getVariable(0));
nameDep.getThrown().connect(methodDep.getThrown());
nameDep.use();
@ -109,13 +105,11 @@ public class UsageGenerator {
nameDep.getResult().connect(hashCodeDep.getVariable(0));
hashCodeDep.getThrown().connect(methodDep.getThrown());
hashCodeDep.use();
return nameDep;
}
private void consumeType(DependencyType type, MethodDependency getClassDep) {
emitPermutation(findClass(type.getName()), getClassDep);
}
private void emitPermutation(ValueType type, MethodDependency getClassDep) {
private void emitPermutation(ValueType type) {
if (!classLoader.isCompileTimeClass(model.getMetaMethod().getClassName()) && !annotationErrorReported) {
annotationErrorReported = true;
diagnostics.error(location, "Metaprogramming method should be within class marked with "
@ -128,28 +122,31 @@ public class UsageGenerator {
return;
}
implRef = buildMethodReference(type);
implRef = buildMethodReference();
model.getUsages().put(type, implRef);
//emitter = new EmitterImpl<>(emitterContext, model.getProxyMethod(), model.getMethod().getReturnType());
MetaprogrammingImpl.templateMethod = model.getMetaMethod();
VariableContext varContext = new TopLevelVariableContext(diagnostics);
MetaprogrammingImpl.generator = new CompositeMethodGenerator(varContext);
MetaprogrammingImpl.varContext = varContext;
MetaprogrammingImpl.returnType = model.getMethod().getReturnType();
/*for (int i = 0; i <= model.getParameters().size(); ++i) {
emitter.generator.getProgram().createVariable();
for (int i = 0; i <= model.getMetaParameterCount(); ++i) {
MetaprogrammingImpl.generator.getProgram().createVariable();
}
*/
Object[] proxyArgs = new Object[model.getMetaParameterCount()];
for (int i = 0; i < proxyArgs.length; ++i) {
if (i == model.getMetaClassParameterIndex()) {
proxyArgs[i] = MetaprogrammingImpl.findClass(type);
} else {
proxyArgs[i] = new ValueImpl<>(null, null, model.getMetaParameterType(i));
proxyArgs[i] = new ValueImpl<>(getParameterVar(i), MetaprogrammingImpl.varContext,
model.getMetaParameterType(i));
}
}
try {
proxyMethod.invoke(null, proxyArgs);
//emitter.close();
Program program = null; //emitter.generator.getProgram();
Program program = MetaprogrammingImpl.generator.getProgram();
//new BoxingEliminator().optimize(program);
ClassHolder cls = new ClassHolder(implRef.getClassName());
@ -176,7 +173,8 @@ public class UsageGenerator {
}
if (model.getClassParameterIndex() >= 0) {
implMethod.getVariable(model.getClassParameterIndex() + 1).connect(getClassDep.getVariable(0));
implMethod.getVariable(model.getClassParameterIndex() + 1).getClassValueNode()
.connect(nameDependency.getVariable(0));
}
if (implMethod.getResult() != null) {
@ -211,15 +209,15 @@ public class UsageGenerator {
}
}
private MethodReference buildMethodReference(ValueType type) {
private MethodReference buildMethodReference() {
if (model.getClassParameterIndex() < 0) {
return new MethodReference(model.getMethod().getClassName() + "$PROXY$" + getSuffix(),
model.getMethod().getDescriptor());
}
int i = 0;
ValueType[] signature = new ValueType[model.getMetaParameterCount()];
for (i = 0; i < signature.length; ++i) {
int i;
ValueType[] signature = new ValueType[model.getMetaParameterCount() + 1];
for (i = 0; i < signature.length - 1; ++i) {
signature[i] = model.getMetaParameterType(i);
}
signature[i] = model.getMethod().getReturnType();
@ -275,9 +273,43 @@ public class UsageGenerator {
throw new AssertionError("Don't know how to map type: " + type);
}
private Variable getParameterVar(int index) {
Program program = MetaprogrammingImpl.generator.getProgram();
Variable var = program.variableAt(index + 1);
ValueType type = model.getMethod().parameterType(index);
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
var = box(var, Boolean.class, boolean.class);
break;
case BYTE:
var = box(var, Byte.class, byte.class);
break;
case SHORT:
var = box(var, Short.class, short.class);
break;
case CHARACTER:
var = box(var, Character.class, char.class);
break;
case INTEGER:
var = box(var, Integer.class, int.class);
break;
case LONG:
var = box(var, Long.class, long.class);
break;
case FLOAT:
var = box(var, Float.class, float.class);
break;
case DOUBLE:
var = box(var, Double.class, double.class);
break;
}
}
return var;
}
private Variable box(Variable var, Class<?> boxed, Class<?> primitive) {
Program program = null; //emitter.generator.getProgram();
Program program = MetaprogrammingImpl.generator.getProgram();
BasicBlock block = program.basicBlockAt(0);
InvokeInstruction insn = new InvokeInstruction();

View File

@ -72,17 +72,18 @@ public class MethodDescriber {
private MethodModel findMetaMethod(MethodReader method) {
ClassReader cls = classSource.get(method.getOwnerName());
boolean isStatic = method.hasModifier(ElementModifier.STATIC);
int expectedParameterCount = (isStatic ? 0 : 1) + method.parameterCount();
for (MethodReader meta : cls.getMethods()) {
if (meta == method
|| !meta.hasModifier(ElementModifier.STATIC)
|| !meta.getName().equals(method.getName())
|| meta.getResultType() != ValueType.VOID
|| meta.parameterCount() != method.parameterCount() + 1) {
|| meta.parameterCount() != expectedParameterCount) {
continue;
}
int paramOffset = 0;
boolean isStatic = method.hasModifier(ElementModifier.STATIC);
if (!isStatic) {
if (meta.parameterCount() == 0 || meta.parameterType(0).isObject(Value.class)) {
return null;

View File

@ -31,6 +31,7 @@ import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.javascript.spi.Generator;
import org.teavm.javascript.spi.InjectedBy;
import org.teavm.javascript.spi.Injector;
import org.teavm.metaprogramming.impl.MetaprogrammingDependencyListener;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
import org.teavm.model.util.*;
@ -354,6 +355,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (wasCancelled()) {
return;
}
dependencyChecker.addDependencyListener(new MetaprogrammingDependencyListener());
AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider();
dependencyChecker.setInterruptor(() -> progressListener.progressReached(0) == TeaVMProgressFeedback.CONTINUE);
dependencyChecker.linkMethod(new MethodReference(Class.class.getName(), "getClass",
@ -473,6 +477,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
}
@SuppressWarnings("WeakerAccess")
public ListableClassHolderSource link(DependencyInfo dependency) {
reportPhase(TeaVMPhase.LINKING, dependency.getReachableClasses().size());
Linker linker = new Linker();

View File

@ -15,7 +15,6 @@
*/
package org.teavm.metaprogramming;
@SuppressWarnings("unused")
public interface Computation<T> {
T compute();
}

View File

@ -39,7 +39,7 @@ public final class Metaprogramming {
return null;
}
public static void exit(Value<?> returnValue) {
public static void exit(Computation<?> returnValue) {
unsupported();
}

View File

@ -24,16 +24,18 @@ import org.junit.runner.RunWith;
import org.teavm.classlib.support.Support_MapTest2;
import org.teavm.junit.TeaVMTestRunner;
@SuppressWarnings({"UnnecessaryUnboxing", "ClassInitializerMayBeStatic", "UnnecessaryTemporaryOnConversionToString",
"MismatchedQueryAndUpdateOfCollection", "StringEquality"})
@RunWith(TeaVMTestRunner.class)
public class LinkedHashMapTest {
LinkedHashMap<Object, Object> hm;
private LinkedHashMap<Object, Object> hm;
final static int hmSize = 1000;
private final static int hmSize = 1000;
static Object[] objArray;
private static Object[] objArray;
static Object[] objArray2;
private static Object[] objArray2;
{
objArray = new Object[hmSize];
objArray2 = new Object[hmSize];
@ -47,14 +49,14 @@ public class LinkedHashMapTest {
hm = new LinkedHashMap<>();
for (int i = 0; i < objArray.length; i++)
hm.put(objArray2[i], objArray[i]);
hm.put("test", null);
hm.put(null, "test");
hm.put("org/teavm/metaprogramming/test", null);
hm.put(null, "org/teavm/metaprogramming/test");
}
@Test
public void test_Constructor() {
// Test for method java.util.LinkedHashMap()
new Support_MapTest2(new LinkedHashMap<String, String>()).runTest();
new Support_MapTest2(new LinkedHashMap<>()).runTest();
LinkedHashMap<Object, Object> hm2 = new LinkedHashMap<>();
assertEquals("Created incorrect LinkedHashMap", 0, hm2.size());
@ -69,6 +71,7 @@ public class LinkedHashMapTest {
new LinkedHashMap<>(-1);
fail("Failed to throw IllegalArgumentException for initial capacity < 0");
} catch (IllegalArgumentException e) {
// as expected
}
LinkedHashMap<Object, Object> empty = new LinkedHashMap<>(0);
@ -86,6 +89,7 @@ public class LinkedHashMapTest {
new LinkedHashMap<>(0, 0);
fail("Failed to throw IllegalArgumentException for initial load factor <= 0");
} catch (IllegalArgumentException e) {
// as expected
}
LinkedHashMap<String, String> empty = new LinkedHashMap<>(0, 0.75f);
assertNull("Empty hashtable access", empty.get("nothing"));
@ -114,8 +118,8 @@ public class LinkedHashMapTest {
assertEquals("Get returned incorecct value for existing key", "HELLO", hm.get("T"));
LinkedHashMap<Object, String> m = new LinkedHashMap<>();
m.put(null, "test");
assertEquals("Failed with null key", "test", m.get(null));
m.put(null, "org/teavm/metaprogramming/test");
assertEquals("Failed with null key", "org/teavm/metaprogramming/test", m.get(null));
assertNull("Failed with missing key matching null hash", m.get(new Integer(0)));
}
@ -128,7 +132,7 @@ public class LinkedHashMapTest {
LinkedHashMap<Number, String> m = new LinkedHashMap<>();
m.put(new Short((short) 0), "short");
m.put(null, "test");
m.put(null, "org/teavm/metaprogramming/test");
m.put(new Integer(0), "int");
assertEquals("Failed adding to bucket containing null", "short", m.get(new Short((short) 0)));
assertEquals("Failed adding to bucket containing null2", "int", m.get(new Integer(0)));
@ -159,7 +163,7 @@ public class LinkedHashMapTest {
assertTrue("Returned set does not contain all keys", s.contains(objArray[i].toString()));
LinkedHashMap<Object, String> m = new LinkedHashMap<>();
m.put(null, "test");
m.put(null, "org/teavm/metaprogramming/test");
assertTrue("Failed with null key", m.keySet().contains(null));
assertNull("Failed with null key", m.keySet().iterator().next());
@ -228,9 +232,9 @@ public class LinkedHashMapTest {
assertNull("Remove of non-existent key returned non-null", hm.remove("LCLCLC"));
LinkedHashMap<Object, String> m = new LinkedHashMap<>();
m.put(null, "test");
m.put(null, "org/teavm/metaprogramming/test");
assertNull("Failed with same hash as null", m.remove(new Integer(0)));
assertEquals("Failed with null key", "test", m.remove(null));
assertEquals("Failed with null key", "org/teavm/metaprogramming/test", m.remove(null));
}
@Test
@ -313,7 +317,7 @@ public class LinkedHashMapTest {
assertTrue("Returned true for invalid key", !hm.containsKey("KKDKDKD"));
LinkedHashMap<Object, String> m = new LinkedHashMap<>();
m.put(null, "test");
m.put(null, "org/teavm/metaprogramming/test");
assertTrue("Failed with null key", m.containsKey(null));
assertTrue("Failed with missing key matching null hash", !m.containsKey(new Integer(0)));
}

View File

@ -24,6 +24,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@SuppressWarnings({"RedundantStringConstructorCall", "RedundantCast"})
@RunWith(TeaVMTestRunner.class)
public class StringTokenizerTest {
@Test
@ -79,7 +80,7 @@ public class StringTokenizerTest {
st.nextElement();
fail("nextElement failed to throw a NoSuchElementException when it should have been out of elements");
} catch (NoSuchElementException e) {
return;
// do nothing
}
}
@ -95,7 +96,7 @@ public class StringTokenizerTest {
st.nextToken();
fail("nextToken failed to throw a NoSuchElementException when it should have been out of elements");
} catch (NoSuchElementException e) {
return;
// do nothing
}
}

View File

@ -38,6 +38,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@SuppressWarnings({"UnnecessaryTemporaryOnConversionToString", "SuspiciousMethodCalls"})
@RunWith(TeaVMTestRunner.class)
public class TreeMapTest {
@ -64,14 +65,13 @@ public class TreeMapTest {
if (null == o1 || null == o2) {
return -1;
}
T c1 = o1;
T c2 = o2;
return c1.compareTo(c2);
return o1.compareTo(o2);
}
}
// Regression for Harmony-1161
class MockComparatorNullTolerable implements Comparator<String> {
@SuppressWarnings("StringEquality")
@Override
public int compare(String o1, String o2) {
if (o1 == o2) {
@ -87,9 +87,9 @@ public class TreeMapTest {
}
}
TreeMap<Object, Object> tm;
private TreeMap<Object, Object> tm;
Object objArray[] = new Object[1000];
private Object[] objArray = new Object[1000];
public TreeMapTest() {
tm = new TreeMap<>();
@ -255,7 +255,7 @@ public class TreeMapTest {
&& head.containsKey("10"));
// Regression for Harmony-1026
TreeMap<Integer, Double> map = new TreeMap<>(new MockComparator<Integer>());
TreeMap<Integer, Double> map = new TreeMap<>(new MockComparator<>());
map.put(1, 2.1);
map.put(2, 3.1);
map.put(3, 4.5);
@ -308,12 +308,14 @@ public class TreeMapTest {
sub.firstKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
try{
sub.lastKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
size = 256;
@ -328,12 +330,14 @@ public class TreeMapTest {
sub.firstKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
try{
sub.lastKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
}
@ -441,7 +445,7 @@ public class TreeMapTest {
assertEquals(3, map.size());
Map<String, String> subMap = map.subMap("", "test");
Map<String, String> subMap = map.subMap("", "org/teavm/metaprogramming/test");
assertEquals(3, subMap.size());
int size = 0;
@ -487,12 +491,14 @@ public class TreeMapTest {
sub.firstKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
try{
sub.lastKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
size = 256;
@ -507,12 +513,14 @@ public class TreeMapTest {
sub.firstKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
try{
sub.lastKey();
fail("java.util.NoSuchElementException should be thrown");
} catch(java.util.NoSuchElementException e) {
// as expected
}
}

View File

@ -18,14 +18,19 @@ package org.teavm.metaprogramming.test;
import static org.junit.Assert.assertEquals;
import static org.teavm.metaprogramming.Metaprogramming.exit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.SkipJVM;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.metaprogramming.CompileTime;
import org.teavm.metaprogramming.Meta;
import org.teavm.metaprogramming.ReflectClass;
import org.teavm.metaprogramming.Value;
@CompileTime
@RunWith(TeaVMTestRunner.class)
public class MetaprogrammingTest {
@Test
@SkipJVM
public void works() {
assertEquals("java.lang.Object".length() + 2, classNameLength(Object.class, 2));
assertEquals("java.lang.Integer".length() + 3, classNameLength(Integer.valueOf(5).getClass(), 3));

View File

@ -19,11 +19,8 @@ import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.diagnostics.Problem;
import org.teavm.jso.JSBody;
import org.teavm.junit.SkipJVM;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.vm.TeaVM;
@ -98,7 +95,7 @@ public class JSOTest {
private List<Problem> build(String methodName) {
TeaVM vm = new TeaVMBuilder().build();
vm.installPlugins();
vm.entryPoint("test", new MethodReference(JSOTest.class, methodName, void.class));
vm.entryPoint("org/teavm/metaprogramming/test", new MethodReference(JSOTest.class, methodName, void.class));
vm.build(new StringBuilder(), null);
return vm.getProblemProvider().getSevereProblems();
}

View File

@ -73,7 +73,6 @@ public class TeaVMTestRunner extends Runner {
private Class<?> testClass;
private ClassHolder classHolder;
private ClassLoader classLoader;
private ClassHolderSource classSource;
private Description suiteDescription;
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
private File outputDir;
@ -88,17 +87,19 @@ public class TeaVMTestRunner extends Runner {
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
synchronized (TeaVMTestRunner.class) {
if (runner != null) {
cleanupFuture = null;
runner.stop();
runner.waitForCompletion();
}
}
}));
}
public TeaVMTestRunner(Class<?> testClass) throws InitializationError {
this.testClass = testClass;
classLoader = TeaVMTestRunner.class.getClassLoader();
classSource = getClassSource(classLoader);
ClassHolderSource classSource = getClassSource(classLoader);
classHolder = classSource.get(testClass.getName());
String outputPath = System.getProperty(PATH_PARAM);
if (outputPath != null) {
@ -157,7 +158,7 @@ public class TeaVMTestRunner extends Runner {
notifier.fireTestFinished(getDescription());
}
protected List<Method> getChildren() {
private List<Method> getChildren() {
List<Method> children = new ArrayList<>();
for (Method method : testClass.getDeclaredMethods()) {
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
@ -168,12 +169,12 @@ public class TeaVMTestRunner extends Runner {
return children;
}
protected Description describeChild(Method child) {
private Description describeChild(Method child) {
return descriptions.computeIfAbsent(child, method -> Description.createTestDescription(testClass,
method.getName()));
}
protected void runChild(Method child, RunNotifier notifier) {
private void runChild(Method child, RunNotifier notifier) {
notifier.fireTestStarted(describeChild(child));
boolean run = false;
@ -261,6 +262,8 @@ public class TeaVMTestRunner extends Runner {
if (!compileResult.success) {
notifier.fireTestFailure(new Failure(description,
new AssertionError(compileResult.errorMessage)));
notifier.fireTestFinished(description);
latch.countDown();
return false;
}