+ */
+public interface ClassResource extends Resource {
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java
new file mode 100644
index 000000000..a884bfac2
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 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.platform.metadata;
+
+import java.util.Map;
+import org.teavm.model.MethodReference;
+import org.teavm.platform.PlatformClass;
+
+/**
+ * Behaviour of this class is similar to {@link MetadataGenerator}. The difference is that method, marked with
+ * {@link ClassScopedMetadataProvider} must take one argument of type {@link PlatformClass}. It will
+ * return different resource for each given class, corresponding to map entries, produced by
+ * {@link #generateMetadata(MetadataGeneratorContext, MethodReference)}.
+ *
+ * @see ClassScopedMetadataProvider
+ * @see MetadataGenerator
+ *
+ * @author Alexey Andreev
+ */
+public interface ClassScopedMetadataGenerator {
+ Map generateMetadata(MetadataGeneratorContext context, MethodReference method);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataProvider.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataProvider.java
new file mode 100644
index 000000000..617cc36fc
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 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.platform.metadata;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Binds a {@link ClassScopedMetadataGenerator} to a native method.
+ *
+ * @see MetadataProvider
+ *
+ * @author Alexey Andreev
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface ClassScopedMetadataProvider {
+ Class extends ClassScopedMetadataGenerator> value();
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java
index 92c0ce73a..5ca4546d6 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java
@@ -50,6 +50,8 @@ import org.teavm.model.MethodReference;
*
* All other types are not considered to be resources and therefore are not accepted.
*
+ * @see ClassScopedMetadataGenerator
+ *
* @author Alexey Andreev
*/
public interface MetadataGenerator {
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java
index 067b4fe26..e505fed5e 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java
@@ -18,6 +18,7 @@ package org.teavm.platform.metadata;
import java.util.Properties;
import org.teavm.common.ServiceRepository;
import org.teavm.model.ListableClassReaderSource;
+import org.teavm.platform.Platform;
import org.teavm.vm.TeaVM;
/**
@@ -48,6 +49,12 @@ public interface MetadataGeneratorContext extends ServiceRepository {
*/
T createResource(Class resourceType);
+ /**
+ * Creates a new resource that represents class literal. Client code then may use
+ * {@link Platform#classFromResource(ClassResource)} to get actual class.
+ */
+ ClassResource createClassResource(String className);
+
/**
* Creates a new resource array.
*/
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java
new file mode 100644
index 000000000..2dfbc2878
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 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.platform.plugin;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.dependency.DependencyAgent;
+import org.teavm.dependency.DependencyPlugin;
+import org.teavm.dependency.MethodDependency;
+import org.teavm.javascript.spi.Generator;
+import org.teavm.javascript.spi.GeneratorContext;
+import org.teavm.model.*;
+import org.teavm.platform.async.AsyncCallback;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class AsyncMethodGenerator implements Generator, DependencyPlugin {
+ private static final MethodReference completeMethod = new MethodReference(AsyncCallback.class, "complete",
+ Object.class, void.class);
+ private static final MethodReference errorMethod = new MethodReference(AsyncCallback.class, "error",
+ Throwable.class, void.class);
+
+ @Override
+ public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
+ MethodReference asyncRef = getAsyncReference(methodRef);
+ writer.append("var callback").ws().append("=").ws().append("function()").ws().append("{}").softNewLine();
+ writer.append("callback.").appendMethod(completeMethod).ws().append("=").ws().append("function($this,").ws()
+ .append("val)").ws().append("{").indent().softNewLine();
+ writer.append("return $return($rt_asyncResult(val));").softNewLine();
+ writer.outdent().append("};").softNewLine();
+ writer.append("callback.").appendMethod(errorMethod).ws().append("=").ws().append("function($this,").ws()
+ .append("e)").ws().append("{").indent().softNewLine();
+ writer.append("return $return($rt_asyncError(e));").softNewLine();
+ writer.outdent().append("};").softNewLine();
+ writer.append("try").ws().append("{").indent().softNewLine();
+ writer.append("return ").appendMethodBody(asyncRef).append('(');
+ ClassReader cls = context.getClassSource().get(methodRef.getClassName());
+ MethodReader method = cls.getMethod(methodRef.getDescriptor());
+ int start = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;
+ for (int i = start; i <= methodRef.parameterCount(); ++i) {
+ writer.append(context.getParameterName(i));
+ writer.append(',').ws();
+ }
+ writer.append("callback);").softNewLine();
+ writer.outdent().append("}").ws().append("catch($e)").ws().append("{").indent().softNewLine();
+ writer.append("return $return($rt_asyncError($e));").softNewLine();
+ writer.outdent().append("}").softNewLine();
+ }
+
+ private MethodReference getAsyncReference(MethodReference methodRef) {
+ ValueType[] signature = new ValueType[methodRef.parameterCount() + 2];
+ for (int i = 0; i < methodRef.parameterCount(); ++i) {
+ signature[i] = methodRef.getDescriptor().parameterType(i);
+ }
+ signature[methodRef.parameterCount()] = ValueType.parse(AsyncCallback.class);
+ signature[methodRef.parameterCount() + 1] = ValueType.VOID;
+ return new MethodReference(methodRef.getClassName(), methodRef.getName(), signature);
+ }
+
+ @Override
+ public void methodAchieved(DependencyAgent checker, MethodDependency method, CallLocation location) {
+ MethodReference asyncRef = getAsyncReference(method.getReference());
+ MethodDependency asyncMethod = checker.linkMethod(asyncRef, location);
+ int paramCount = method.getReference().parameterCount();
+ for (int i = 0; i <= paramCount; ++i) {
+ method.getVariable(i).connect(asyncMethod.getVariable(i));
+ }
+ asyncMethod.use();
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodProcessor.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodProcessor.java
new file mode 100644
index 000000000..9bc0b6564
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/AsyncMethodProcessor.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 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.platform.plugin;
+
+import org.teavm.dependency.PluggableDependency;
+import org.teavm.diagnostics.Diagnostics;
+import org.teavm.javascript.spi.Async;
+import org.teavm.javascript.spi.GeneratedBy;
+import org.teavm.model.*;
+import org.teavm.platform.async.AsyncCallback;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class AsyncMethodProcessor implements ClassHolderTransformer {
+ @Override
+ public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
+ for (MethodHolder method : cls.getMethods()) {
+ if (method.hasModifier(ElementModifier.NATIVE) &&
+ method.getAnnotations().get(Async.class.getName()) != null &&
+ method.getAnnotations().get(GeneratedBy.class.getName()) == null) {
+ ValueType[] signature = new ValueType[method.parameterCount() + 2];
+ for (int i = 0; i < method.parameterCount(); ++i) {
+ signature[i] = method.parameterType(i);
+ }
+ signature[method.parameterCount()] = ValueType.parse(AsyncCallback.class);
+ signature[method.parameterCount() + 1] = ValueType.VOID;
+ MethodDescriptor asyncDesc = new MethodDescriptor(method.getName(), signature);
+ MethodHolder asyncMethod = cls.getMethod(asyncDesc);
+ if (asyncMethod != null) {
+ if (asyncMethod.hasModifier(ElementModifier.STATIC) !=
+ method.hasModifier(ElementModifier.STATIC)) {
+ diagnostics.error(new CallLocation(method.getReference()), "Methods {{m0}} and {{m1}} must " +
+ "both be either static or non-static",
+ method.getReference(), asyncMethod.getReference());
+ }
+ AnnotationHolder annot = new AnnotationHolder(GeneratedBy.class.getName());
+ annot.getValues().put("value", new AnnotationValue(ValueType.parse(AsyncMethodGenerator.class)));
+ method.getAnnotations().add(annot);
+ annot = new AnnotationHolder(PluggableDependency.class.getName());
+ annot.getValues().put("value", new AnnotationValue(ValueType.parse(AsyncMethodGenerator.class)));
+ method.getAnnotations().add(annot);
+ }
+ }
+ }
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeClassResource.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeClassResource.java
new file mode 100644
index 000000000..2e9ac00dd
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeClassResource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 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.platform.plugin;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.platform.metadata.ClassResource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeClassResource implements ClassResource, ResourceWriter {
+ private String className;
+
+ public BuildTimeClassResource(String className) {
+ this.className = className;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ @Override
+ public void write(SourceWriter writer) throws IOException {
+ writer.appendClass(className);
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java
index a936fee0f..a1cda00fd 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java
@@ -102,8 +102,8 @@ class BuildTimeResourceProxyBuilder {
private void scanIface(Class> iface) {
if (!Resource.class.isAssignableFrom(iface)) {
- throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() +
- ". This type does not implement the " + Resource.class.getName() + " interface");
+ throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + "." +
+ " This type does not implement the " + Resource.class.getName() + " interface");
}
// Scan methods
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java
index dead2901c..b24c0547d 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java
@@ -36,7 +36,7 @@ class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod {
if (i > 0) {
writer.append(',').ws();
}
- ResourceWriterHelper.writeString(writer, propertyNames[i]);
+ ResourceWriterHelper.writeIdentifier(writer, propertyNames[i]);
writer.ws().append(':').ws();
ResourceWriterHelper.write(writer, proxy.data[i]);
}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java
similarity index 90%
rename from teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java
rename to teavm-platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java
index 4d4238300..a57d59b02 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ClassLookupDependencySupport.java
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.teavm.classlib.impl;
+package org.teavm.platform.plugin;
import org.teavm.dependency.*;
import org.teavm.model.*;
+import org.teavm.platform.Platform;
/**
*
@@ -38,14 +39,14 @@ public class ClassLookupDependencySupport implements DependencyListener {
@Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReference ref = method.getReference();
- if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) {
+ if (ref.getClassName().equals(Platform.class.getName()) && ref.getName().equals("lookupClass")) {
allClasses.addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) {
ClassReader cls = agent.getClassSource().get(type.getName());
if (cls == null) {
return;
}
- MethodReader initMethod = cls.getMethod(new MethodDescriptor("", ValueType.VOID));
+ MethodReader initMethod = cls.getMethod(new MethodDescriptor("", void.class));
if (initMethod != null) {
agent.linkMethod(initMethod.getReference(), location).use();
}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java
new file mode 100644
index 000000000..eef273ead
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 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.platform.plugin;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.javascript.Renderer;
+import org.teavm.javascript.spi.Generator;
+import org.teavm.javascript.spi.GeneratorContext;
+import org.teavm.model.*;
+import org.teavm.platform.metadata.ClassScopedMetadataGenerator;
+import org.teavm.platform.metadata.ClassScopedMetadataProvider;
+import org.teavm.platform.metadata.Resource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class ClassScopedMetadataProviderNativeGenerator implements Generator {
+ @Override
+ public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
+ // Validate method
+ ClassReader cls = context.getClassSource().get(methodRef.getClassName());
+ MethodReader method = cls.getMethod(methodRef.getDescriptor());
+ AnnotationReader providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName());
+ if (providerAnnot == null) {
+ return;
+ }
+ if (!method.hasModifier(ElementModifier.NATIVE)) {
+ context.getDiagnostics().error(new CallLocation(methodRef), "Method {{m0}} is marked with " +
+ "{{c1}} annotation, but it is not native", methodRef, ClassScopedMetadataProvider.class.getName());
+ return;
+ }
+
+ // Find and instantiate metadata generator
+ ValueType generatorType = providerAnnot.getValue("value").getJavaClass();
+ String generatorClassName = ((ValueType.Object)generatorType).getClassName();
+ Class> generatorClass;
+ try {
+ generatorClass = Class.forName(generatorClassName, true, context.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ context.getDiagnostics().error(new CallLocation(methodRef), "Can't find metadata provider class {{c0}}",
+ generatorClassName);
+ return;
+ }
+ Constructor> cons;
+ try {
+ cons = generatorClass.getConstructor();
+ } catch (NoSuchMethodException e) {
+ context.getDiagnostics().error(new CallLocation(methodRef), "Metadata generator {{c0}} does not have " +
+ "a public no-arg constructor", generatorClassName);
+ return;
+ }
+ ClassScopedMetadataGenerator generator;
+ try {
+ generator = (ClassScopedMetadataGenerator)cons.newInstance();
+ } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
+ context.getDiagnostics().error(new CallLocation(methodRef), "Error instantiating metadata " +
+ "generator {{c0}}", generatorClassName);
+ return;
+ }
+ DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
+ context.getClassLoader(), context.getProperties(), context);
+
+ Map resourceMap = generator.generateMetadata(metadataContext, methodRef);
+ writer.append("var p").ws().append("=").ws().append("\"" + Renderer.escapeString("$$res_" +
+ writer.getNaming().getNameFor(methodRef)) + "\"").append(";").softNewLine();
+ for (Map.Entry entry : resourceMap.entrySet()) {
+ writer.appendClass(entry.getKey()).append("[p]").ws().append("=").ws();
+ ResourceWriterHelper.write(writer, entry.getValue());
+ writer.append(";").softNewLine();
+ }
+ writer.appendMethodBody(methodRef).ws().append('=').ws().append("function(cls)").ws().append("{")
+ .softNewLine().indent();
+ writer.append("return cls.hasOwnProperty(p)").ws().append("?").ws().append("cls[p]").ws().append(":")
+ .ws().append("null;").softNewLine();
+ writer.outdent().append("}").softNewLine();
+ writer.append("return ").appendMethodBody(methodRef).append("(").append(context.getParameterName(1))
+ .append(");").softNewLine();
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java
index 947202715..049fe912b 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java
@@ -19,10 +19,7 @@ import java.lang.reflect.Proxy;
import java.util.Properties;
import org.teavm.common.ServiceRepository;
import org.teavm.model.ListableClassReaderSource;
-import org.teavm.platform.metadata.MetadataGeneratorContext;
-import org.teavm.platform.metadata.Resource;
-import org.teavm.platform.metadata.ResourceArray;
-import org.teavm.platform.metadata.ResourceMap;
+import org.teavm.platform.metadata.*;
/**
*
@@ -70,6 +67,11 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
return new BuildTimeResourceArray<>();
}
+ @Override
+ public ClassResource createClassResource(String className) {
+ return new BuildTimeClassResource(className);
+ }
+
@Override
public ResourceMap createResourceMap() {
return new BuildTimeResourceMap<>();
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java
similarity index 93%
rename from teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java
rename to teavm-platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java
index 190defd64..fe0deb778 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.teavm.classlib.impl;
+package org.teavm.platform.plugin;
import org.teavm.dependency.*;
import org.teavm.model.CallLocation;
@@ -21,6 +21,7 @@ import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.ValueType;
+import org.teavm.platform.Platform;
/**
*
@@ -53,8 +54,8 @@ public class EnumDependencySupport implements DependencyListener {
@Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
- if (method.getReference().getClassName().equals("java.lang.Class") &&
- method.getReference().getName().equals("getEnumConstantsImpl")) {
+ if (method.getReference().getClassName().equals(Platform.class.getName()) &&
+ method.getReference().getName().equals("getEnumConstants")) {
unlocked = true;
allEnums.connect(method.getResult().getArrayItem());
method.getResult().propagate(agent.getType("[java.lang.Enum"));
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/EnumTransformer.java
similarity index 93%
rename from teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java
rename to teavm-platform/src/main/java/org/teavm/platform/plugin/EnumTransformer.java
index 4f6355827..4be7e36a0 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/EnumTransformer.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.teavm.classlib.impl;
+package org.teavm.platform.plugin;
import org.teavm.diagnostics.Diagnostics;
-import org.teavm.javascript.ni.PreserveOriginalName;
+import org.teavm.javascript.spi.PreserveOriginalName;
import org.teavm.model.*;
/**
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java
index 6ddda44ca..3d44de603 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java
@@ -19,8 +19,8 @@ import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.teavm.codegen.SourceWriter;
-import org.teavm.javascript.ni.Generator;
-import org.teavm.javascript.ni.GeneratorContext;
+import org.teavm.javascript.spi.Generator;
+import org.teavm.javascript.spi.GeneratorContext;
import org.teavm.model.*;
import org.teavm.platform.metadata.MetadataGenerator;
import org.teavm.platform.metadata.MetadataProvider;
@@ -41,8 +41,9 @@ public class MetadataProviderNativeGenerator implements Generator {
return;
}
if (!method.hasModifier(ElementModifier.NATIVE)) {
- throw new IllegalStateException("Method " + method.getReference() + " was marked with " +
- MetadataProvider.class.getName() + " but it is not native");
+ context.getDiagnostics().error(new CallLocation(methodRef), "Method {{m0}} is marked with " +
+ "{{c1}} annotation, but it is not native", methodRef, MetadataProvider.class.getName());
+ return;
}
// Find and instantiate metadata generator
@@ -52,23 +53,25 @@ public class MetadataProviderNativeGenerator implements Generator {
try {
generatorClass = Class.forName(generatorClassName, true, context.getClassLoader());
} catch (ClassNotFoundException e) {
- throw new RuntimeException("Can't find metadata generator class: " + generatorClassName, e);
+ context.getDiagnostics().error(new CallLocation(methodRef), "Can't find metadata provider class {{c0}}",
+ generatorClassName);
+ return;
}
Constructor> cons;
try {
cons = generatorClass.getConstructor();
} catch (NoSuchMethodException e) {
- throw new RuntimeException("Metadata generator " + generatorClassName + " does not have a public " +
- "no-arg constructor", e);
+ context.getDiagnostics().error(new CallLocation(methodRef), "Metadata generator {{c0}} does not have " +
+ "a public no-arg constructor", generatorClassName);
+ return;
}
MetadataGenerator generator;
try {
generator = (MetadataGenerator)cons.newInstance();
- } catch (IllegalAccessException | InstantiationException e) {
- throw new RuntimeException("Error instantiating metadata generator " + generatorClassName, e);
- } catch (InvocationTargetException e) {
- throw new RuntimeException("Error instantiating metadata generator " + generatorClassName,
- e.getTargetException());
+ } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
+ context.getDiagnostics().error(new CallLocation(methodRef), "Error instantiating metadata " +
+ "generator {{c0}}", generatorClassName);
+ return;
}
DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
context.getClassLoader(), context.getProperties(), context);
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java
index 3ad502b9e..0bf498544 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java
@@ -16,8 +16,10 @@
package org.teavm.platform.plugin;
import org.teavm.diagnostics.Diagnostics;
-import org.teavm.javascript.ni.GeneratedBy;
+import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.model.*;
+import org.teavm.platform.PlatformClass;
+import org.teavm.platform.metadata.ClassScopedMetadataProvider;
import org.teavm.platform.metadata.MetadataProvider;
/**
@@ -29,13 +31,26 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
for (MethodHolder method : cls.getMethods()) {
AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
- if (providerAnnot == null) {
- continue;
+ if (providerAnnot != null) {
+ AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
+ genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
+ MetadataProviderNativeGenerator.class.getName())));
+ method.getAnnotations().add(genAnnot);
+ }
+ providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName());
+ if (providerAnnot != null) {
+ ValueType[] params = method.getParameterTypes();
+ if (params.length != 1 && params[0].isObject(PlatformClass.class.getName())) {
+ diagnostics.error(new CallLocation(method.getReference()), "Method {{m0}} marked with {{c1}} " +
+ "must take exactly one parameter of type {{c2}}",
+ method.getReference(), ClassScopedMetadataProvider.class.getName(),
+ PlatformClass.class.getName());
+ }
+ AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
+ genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
+ ClassScopedMetadataProviderNativeGenerator.class.getName())));
+ method.getAnnotations().add(genAnnot);
}
- AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
- genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
- MetadataProviderNativeGenerator.class.getName())));
- method.getAnnotations().add(genAnnot);
}
}
}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java
similarity index 83%
rename from teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java
rename to teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java
index e94490899..14897e80b 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/NewInstanceDependencySupport.java
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.teavm.classlib.impl;
+package org.teavm.platform.plugin;
import org.teavm.dependency.*;
import org.teavm.model.*;
+import org.teavm.platform.Platform;
/**
*
@@ -39,7 +40,7 @@ public class NewInstanceDependencySupport implements DependencyListener {
if (cls.hasModifier(ElementModifier.ABSTRACT) || cls.hasModifier(ElementModifier.INTERFACE)) {
return;
}
- MethodReader method = cls.getMethod(new MethodDescriptor("", ValueType.VOID));
+ MethodReader method = cls.getMethod(new MethodDescriptor("", void.class));
if (method != null) {
allClassesNode.propagate(agent.getType(className));
}
@@ -48,7 +49,7 @@ public class NewInstanceDependencySupport implements DependencyListener {
@Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReader reader = method.getMethod();
- if (reader.getOwnerName().equals("java.lang.Class") && reader.getName().equals("newInstance")) {
+ if (reader.getOwnerName().equals(Platform.class.getName()) && reader.getName().equals("newInstance")) {
allClassesNode.connect(method.getResult());
method.getResult().addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) {
@@ -59,8 +60,10 @@ public class NewInstanceDependencySupport implements DependencyListener {
}
private void attachConstructor(DependencyAgent checker, String type, CallLocation location) {
- MethodReference ref = new MethodReference(type, new MethodDescriptor("", ValueType.VOID));
- checker.linkMethod(ref, location).use();
+ MethodReference ref = new MethodReference(type, "", ValueType.VOID);
+ MethodDependency methodDep = checker.linkMethod(ref, location);
+ methodDep.getVariable(0).propagate(checker.getType(type));
+ methodDep.use();
}
@Override
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java
new file mode 100644
index 000000000..5d812c02f
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2015 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.platform.plugin;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.dependency.DependencyAgent;
+import org.teavm.dependency.DependencyPlugin;
+import org.teavm.dependency.MethodDependency;
+import org.teavm.javascript.spi.Generator;
+import org.teavm.javascript.spi.GeneratorContext;
+import org.teavm.javascript.spi.Injector;
+import org.teavm.javascript.spi.InjectorContext;
+import org.teavm.model.*;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class PlatformGenerator implements Generator, Injector, DependencyPlugin {
+ @Override
+ public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
+ switch (method.getReference().getName()) {
+ case "asJavaClass":
+ method.getResult().propagate(agent.getType("java.lang.Class"));
+ return;
+ case "clone":
+ method.getVariable(0).connect(method.getResult());
+ break;
+ }
+ }
+
+ @Override
+ public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
+ switch (methodRef.getName()) {
+ case "asJavaClass":
+ case "classFromResource":
+ context.writeExpr(context.getArgument(0));
+ return;
+ case "getEnumConstants":
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append(".values()");
+ break;
+ }
+ }
+
+ @Override
+ public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
+ switch (methodRef.getName()) {
+ case "newInstance":
+ generateNewInstance(context, writer, methodRef);
+ break;
+ case "lookupClass":
+ generateLookup(context, writer);
+ break;
+ case "clone":
+ generateClone(context, writer);
+ break;
+ }
+ }
+
+ private void generateNewInstance(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
+ throws IOException {
+ writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine();
+ for (String clsName : context.getClassSource().getClassNames()) {
+ ClassReader cls = context.getClassSource().get(clsName);
+ MethodReader method = cls.getMethod(new MethodDescriptor("", void.class));
+ if (method != null) {
+ writer.appendClass(clsName).append("[c]").ws().append("=").ws()
+ .append(writer.getNaming().getNameForInit(method.getReference()))
+ .append(";").softNewLine();
+ }
+ }
+ writer.appendMethodBody(methodRef).ws().append("=").ws().append("function(cls)").ws().append("{")
+ .softNewLine().indent();
+ writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
+ writer.append("return null;").softNewLine();
+ writer.outdent().append("}").softNewLine();
+ writer.append("return cls[c]();").softNewLine();
+ writer.outdent().append("}").softNewLine();
+ writer.append("return ").appendMethodBody(methodRef).append("(")
+ .append(context.getParameterName(1)).append(");").softNewLine();
+ }
+
+ private void generateLookup(GeneratorContext context, SourceWriter writer) throws IOException {
+ String param = context.getParameterName(1);
+ writer.append("switch ($rt_ustr(" + param + ")) {").softNewLine().indent();
+ for (String name : context.getClassSource().getClassNames()) {
+ writer.append("case \"" + name + "\": ").appendClass(name).append(".$clinit(); ")
+ .append("return ").appendClass(name).append(";").softNewLine();
+ }
+ writer.append("default: return null;").softNewLine();
+ writer.outdent().append("}").softNewLine();
+ }
+
+ private void generateClone(GeneratorContext context, SourceWriter writer) throws IOException {
+ String obj = context.getParameterName(1);
+ writer.append("var copy").ws().append("=").ws().append("new ").append(obj).append(".constructor();")
+ .softNewLine();
+ writer.append("for").ws().append("(var field in " + obj + ")").ws().append("{").softNewLine().indent();
+ writer.append("if").ws().append("(!" + obj + ".hasOwnProperty(field))").ws().append("{").softNewLine().indent();
+ writer.append("continue;").softNewLine().outdent().append("}").softNewLine();
+ writer.append("copy[field]").ws().append("=").ws().append(obj).append("[field];")
+ .softNewLine().outdent().append("}").softNewLine();
+ writer.append("return copy;").softNewLine();
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java
index ad16bfc25..710e0c718 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java
@@ -29,5 +29,10 @@ public class PlatformPlugin implements TeaVMPlugin {
host.add(new ResourceTransformer());
host.add(new ResourceAccessorTransformer(host));
host.add(new ResourceAccessorDependencyListener());
+ host.add(new AsyncMethodProcessor());
+ host.add(new NewInstanceDependencySupport());
+ host.add(new ClassLookupDependencySupport());
+ host.add(new EnumDependencySupport());
+ host.add(new EnumTransformer());
}
}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
index 62c267ad5..18337035c 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
@@ -18,8 +18,8 @@ package org.teavm.platform.plugin;
import java.io.IOException;
import org.teavm.javascript.ast.ConstantExpr;
import org.teavm.javascript.ast.Expr;
-import org.teavm.javascript.ni.Injector;
-import org.teavm.javascript.ni.InjectorContext;
+import org.teavm.javascript.spi.Injector;
+import org.teavm.javascript.spi.InjectorContext;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java
index af816217f..715d248ba 100644
--- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java
@@ -45,6 +45,34 @@ final class ResourceWriterHelper {
}
}
+ public static void writeIdentifier(SourceWriter writer, String id) throws IOException {
+ if (id.isEmpty() || !isIdentifierStart(id.charAt(0))) {
+ writeString(writer, id);
+ return;
+ }
+ for (int i = 1; i < id.length(); ++i) {
+ if (isIdentifierPart(id.charAt(i))) {
+ writeString(writer, id);
+ return;
+ }
+ }
+ writer.append(id);
+ }
+
+ private static boolean isIdentifierStart(char c) {
+ if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
+ return true;
+ }
+ return c == '$' || c == '_';
+ }
+
+ private static boolean isIdentifierPart(char c) {
+ if (isIdentifierStart(c)) {
+ return true;
+ }
+ return c >= '0' && c <= '9';
+ }
+
public static void writeString(SourceWriter writer, String s) throws IOException {
writer.append('"');
for (int i = 0; i < s.length(); ++i) {
diff --git a/teavm-samples/teavm-samples-benchmark/pom.xml b/teavm-samples/teavm-samples-benchmark/pom.xml
index 711bcca06..bbf7e22d0 100644
--- a/teavm-samples/teavm-samples-benchmark/pom.xml
+++ b/teavm-samples/teavm-samples-benchmark/pom.xml
@@ -91,7 +91,7 @@
${project.build.directory}/generated/js/teavm
org.teavm.samples.benchmark.teavm.BenchmarkStarter
SEPARATE
- false
+ true
true
diff --git a/teavm-tests/.gitignore b/teavm-tests/.gitignore
new file mode 100644
index 000000000..c708c363d
--- /dev/null
+++ b/teavm-tests/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.settings
+/.classpath
+/.project
diff --git a/teavm-tests/pom.xml b/teavm-tests/pom.xml
new file mode 100644
index 000000000..806173c02
--- /dev/null
+++ b/teavm-tests/pom.xml
@@ -0,0 +1,115 @@
+
+
+ 4.0.0
+
+
+ org.teavm
+ teavm
+ 0.3.0-SNAPSHOT
+
+ teavm-tests
+
+ TeaVM tests
+ Project containing TeaVM tests, as it is impossible to test each module separately
+
+
+
+ org.teavm
+ teavm-core
+ ${project.version}
+
+
+ org.teavm
+ teavm-classlib
+ ${project.version}
+
+
+ org.teavm
+ teavm-platform
+ ${project.version}
+
+
+ org.teavm
+ teavm-jso
+ ${project.version}
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+
+ org.teavm
+ teavm-maven-plugin
+ ${project.version}
+
+
+ org.teavm
+ teavm-platform
+ ${project.version}
+
+
+
+
+ generate-javascript-tests
+
+ build-test-javascript
+
+ process-test-classes
+
+ false
+
+ en, en_US, en_GB, ru, ru_RU
+
+ ${teavm.test.incremental}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org/teavm/platform/**/*.java
+ org/teavm/jso/**/*.java
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ ../checkstyle.xml
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
\ No newline at end of file
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedReaderTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/io/BufferedReaderTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedReaderTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/io/BufferedReaderTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/io/InputStreamReaderTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/io/InputStreamReaderTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/io/InputStreamReaderTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/io/InputStreamReaderTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/io/PushbackInputStreamTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/io/PushbackInputStreamTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/io/PushbackInputStreamTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/io/PushbackInputStreamTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/CharacterTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/CharacterTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/CharacterTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/CharacterTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ClassTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ClassTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/EnumTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/EnumTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/EnumTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/EnumTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/FloatTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/FloatTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/IntegerTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/IntegerTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/IntegerTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/IntegerTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/MathTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/MathTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/MathTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/MathTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ObjectTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ObjectTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ObjectTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/ObjectTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/StringTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/StringTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/SystemTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/SystemTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/SystemTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/SystemTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/TestObject.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/TestObject.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/TestObject.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/TestObject.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/ThreadTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/VMTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/VMTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/VMTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/VMTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalArithmeticTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalCompareTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalConstructorsTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalConvertTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigDecimalScaleOperationsTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerAddTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerAndTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerCompareTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerConstructorsTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerConvertTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerDivideTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerHashCodeTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerModPowTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerMultiplyTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerNotTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOrTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerSubtractTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerToStringTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/math/BigIntegerXorTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/ByteBufferWrapperTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferWrapperTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/ByteBufferWrapperTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferWrapperTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/CharBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/CharBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/CharBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/CharBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/DoubleBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/DoubleBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/DoubleBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/DoubleBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/FloatBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/FloatBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/FloatBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/FloatBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/IntBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/IntBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/IntBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/IntBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/LongBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/LongBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/LongBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/LongBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/nio/ShortBufferTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/nio/ShortBufferTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/nio/ShortBufferTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/nio/ShortBufferTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/text/DateFormatTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/text/DateFormatTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/text/DateFormatTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/text/DateFormatTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/text/SimpleDateFormatTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/text/SimpleDateFormatTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/text/SimpleDateFormatTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/text/SimpleDateFormatTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArraysTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/ArraysTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArraysTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/ArraysTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/BitSetTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/BitSetTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/BitSetTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/BitSetTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/CollectionsTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/CollectionsTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/CollectionsTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/CollectionsTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/HashtableTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/HashtableTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/HashtableTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/LinkedHashMapTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LocaleTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/LocaleTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/LocaleTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/LocaleTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/PriorityQueueTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/PriorityQueueTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/PriorityQueueTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/PriorityQueueTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ServiceLoaderTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/ServiceLoaderTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/ServiceLoaderTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/ServiceLoaderTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TestService.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/TestService.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/TestService.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/TestService.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TestServiceImpl.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/TestServiceImpl.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/TestServiceImpl.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/TestServiceImpl.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/VectorTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/VectorTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/VectorTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/VectorTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/Matcher2Test.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/Matcher2Test.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/Matcher2Test.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/Matcher2Test.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/MatcherTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/MatcherTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/MatcherTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/MatcherTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/ModeTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/ModeTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/ModeTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/ModeTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/Pattern2Test.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/Pattern2Test.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/Pattern2Test.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/Pattern2Test.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/PatternErrorTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/PatternErrorTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/PatternErrorTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/PatternErrorTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/PatternSyntaxExceptionTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/PatternSyntaxExceptionTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/PatternSyntaxExceptionTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/PatternSyntaxExceptionTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/PatternTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/PatternTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/PatternTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/PatternTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/ReplaceTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/ReplaceTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/ReplaceTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/ReplaceTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/SplitTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/SplitTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/regex/SplitTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/regex/SplitTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/zip/GZIPInputStreamTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/zip/GZIPInputStreamTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/java/util/zip/GZIPInputStreamTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/java/util/zip/GZIPInputStreamTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_CollectionTest.java b/teavm-tests/src/test/java/org/teavm/classlib/support/Support_CollectionTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/support/Support_CollectionTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/support/Support_CollectionTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_ListTest.java b/teavm-tests/src/test/java/org/teavm/classlib/support/Support_ListTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/support/Support_ListTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/support/Support_ListTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_MapTest2.java b/teavm-tests/src/test/java/org/teavm/classlib/support/Support_MapTest2.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/support/Support_MapTest2.java
rename to teavm-tests/src/test/java/org/teavm/classlib/support/Support_MapTest2.java
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/support/Support_UnmodifiableCollectionTest.java b/teavm-tests/src/test/java/org/teavm/classlib/support/Support_UnmodifiableCollectionTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/classlib/support/Support_UnmodifiableCollectionTest.java
rename to teavm-tests/src/test/java/org/teavm/classlib/support/Support_UnmodifiableCollectionTest.java
diff --git a/teavm-jso/src/test/java/org/teavm/jso/test/JSOTest.java b/teavm-tests/src/test/java/org/teavm/jso/test/JSOTest.java
similarity index 100%
rename from teavm-jso/src/test/java/org/teavm/jso/test/JSOTest.java
rename to teavm-tests/src/test/java/org/teavm/jso/test/JSOTest.java
diff --git a/teavm-jso/src/test/java/org/teavm/jso/test/RegExp.java b/teavm-tests/src/test/java/org/teavm/jso/test/RegExp.java
similarity index 100%
rename from teavm-jso/src/test/java/org/teavm/jso/test/RegExp.java
rename to teavm-tests/src/test/java/org/teavm/jso/test/RegExp.java
diff --git a/teavm-jso/src/test/java/org/teavm/jso/test/Window.java b/teavm-tests/src/test/java/org/teavm/jso/test/Window.java
similarity index 100%
rename from teavm-jso/src/test/java/org/teavm/jso/test/Window.java
rename to teavm-tests/src/test/java/org/teavm/jso/test/Window.java
diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java b/teavm-tests/src/test/java/org/teavm/platform/metadata/DependentTestResource.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java
rename to teavm-tests/src/test/java/org/teavm/platform/metadata/DependentTestResource.java
diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-tests/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java
rename to teavm-tests/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java
diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java b/teavm-tests/src/test/java/org/teavm/platform/metadata/TestResource.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java
rename to teavm-tests/src/test/java/org/teavm/platform/metadata/TestResource.java
diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java b/teavm-tests/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java
similarity index 100%
rename from teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java
rename to teavm-tests/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java
diff --git a/teavm-classlib/src/test/resources/META-INF/services/org.teavm.classlib.java.util.TestService b/teavm-tests/src/test/resources/META-INF/services/org.teavm.classlib.java.util.TestService
similarity index 100%
rename from teavm-classlib/src/test/resources/META-INF/services/org.teavm.classlib.java.util.TestService
rename to teavm-tests/src/test/resources/META-INF/services/org.teavm.classlib.java.util.TestService