From 43aebc41743f7d713b916948060bbe0ab041c826 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Thu, 29 May 2014 21:17:57 +0400 Subject: [PATCH 01/16] Adds initial interface for platform resource generation --- pom.xml | 1 + teavm-platform/.gitignore | 4 ++++ teavm-platform/pom.xml | 19 +++++++++++++++++++ .../platform/metadata/MetadataGenerator.java | 11 +++++++++++ .../metadata/MetadataGeneratorContext.java | 13 +++++++++++++ .../platform/metadata/MetadataProvider.java | 16 ++++++++++++++++ .../org/teavm/platform/metadata/Resource.java | 15 +++++++++++++++ .../platform/metadata/ResourceArray.java | 13 +++++++++++++ .../teavm/platform/metadata/ResourceMap.java | 11 +++++++++++ .../teavm/platform/plugin/PlatformPlugin.java | 14 ++++++++++++++ .../services/org.teavm.vm.spi.TeaVMPlugin | 1 + 11 files changed, 118 insertions(+) create mode 100644 teavm-platform/.gitignore create mode 100644 teavm-platform/pom.xml create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java create mode 100644 teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin diff --git a/pom.xml b/pom.xml index cb91e354c..c144930dc 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ teavm-dom teavm-jso teavm-html4j + teavm-platform diff --git a/teavm-platform/.gitignore b/teavm-platform/.gitignore new file mode 100644 index 000000000..bb138cf03 --- /dev/null +++ b/teavm-platform/.gitignore @@ -0,0 +1,4 @@ +/target +/.settings +/.project +/.classpath diff --git a/teavm-platform/pom.xml b/teavm-platform/pom.xml new file mode 100644 index 000000000..f35121baa --- /dev/null +++ b/teavm-platform/pom.xml @@ -0,0 +1,19 @@ + + 4.0.0 + + + org.teavm + teavm + 0.2-SNAPSHOT + + teavm-platform + + + + org.teavm + teavm-core + ${project.version} + + + \ No newline at end of file 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 new file mode 100644 index 000000000..bf7302399 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java @@ -0,0 +1,11 @@ +package org.teavm.platform.metadata; + +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public interface MetadataGenerator { + Object generateMetadata(MetadataGeneratorContext context, MethodReference method); +} 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 new file mode 100644 index 000000000..c172938d9 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java @@ -0,0 +1,13 @@ +package org.teavm.platform.metadata; + +import org.teavm.model.ListableClassReaderSource; + +/** + * + * @author Alexey Andreev + */ +public interface MetadataGeneratorContext { + ListableClassReaderSource getClassSource(); + + T createResource(Class resourceType); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java new file mode 100644 index 000000000..aed053db3 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java @@ -0,0 +1,16 @@ +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; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface MetadataProvider { + Class value(); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java new file mode 100644 index 000000000..64755a4d6 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java @@ -0,0 +1,15 @@ +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; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Resource { +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java new file mode 100644 index 000000000..43bd6a7d0 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java @@ -0,0 +1,13 @@ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface ResourceArray { + int size(); + + T get(int i); + + void add(T elem); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java new file mode 100644 index 000000000..eddd2e28c --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java @@ -0,0 +1,11 @@ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface ResourceMap { + T get(String key); + + void put(String key, T value); +} 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 new file mode 100644 index 000000000..09f41abc6 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -0,0 +1,14 @@ +package org.teavm.platform.plugin; + +import org.teavm.vm.spi.TeaVMHost; +import org.teavm.vm.spi.TeaVMPlugin; + +/** + * + * @author Alexey Andreev + */ +public class PlatformPlugin implements TeaVMPlugin { + @Override + public void install(TeaVMHost host) { + } +} diff --git a/teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin b/teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin new file mode 100644 index 000000000..03f8f7273 --- /dev/null +++ b/teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin @@ -0,0 +1 @@ +org.teavm.platform.plugin.PlatformPlugin \ No newline at end of file From a533e6f11261387e088c25afd93b76823e53bc2d Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 2 Jun 2014 22:18:00 +0400 Subject: [PATCH 02/16] Adds documentation and applies license --- .../platform/metadata/MetadataGenerator.java | 54 +++++++++++++++++++ .../metadata/MetadataGeneratorContext.java | 46 ++++++++++++++++ .../platform/metadata/MetadataProvider.java | 16 ++++++ .../org/teavm/platform/metadata/Resource.java | 22 ++++++++ .../platform/metadata/ResourceArray.java | 15 ++++++ .../teavm/platform/metadata/ResourceMap.java | 15 ++++++ .../teavm/platform/plugin/PlatformPlugin.java | 15 ++++++ .../teavm/platform/plugin/ResourceProxy.java | 30 +++++++++++ 8 files changed, 213 insertions(+) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java 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 bf7302399..8a5a0e918 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 @@ -1,11 +1,65 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.metadata; import org.teavm.model.MethodReference; /** + *

Represents a generator, that produces resources during compilation phase. User must implement this + * interface and bind this implementation to a method that would read resources at runtime.

+ * + *

Here is the full workflow:

+ * + *
    + *
  • Compiler finds a method that is marked with the {@link MetadataProvider} annotation. + * This method must be declared as native, otherwise compiler should throw an exception.
  • + *
  • Compiler instantiates the {@link MetadataGenerator} instance with the no-arg constructor + * If no such constructor exists, compiler throws exception.
  • + *
  • Compiler runs the {@link #generateMetadata(MetadataGeneratorContext, MethodReference)} method + * ands gets the produced resource.
  • + *
  • Compiler generates implementation of the method marked with {@link MetadataProvider}, that + * will return the generated resource in run time.
  • + *
+ * + *

Therefore, the type of the value, returned by the + * {@link #generateMetadata(MetadataGeneratorContext, MethodReference)} + * method must match the returning type of the appropriate method, marked with {@link MetadataProvider}.

+ * + *

The valid resource types are the following:

+ * + *
    + *
  • Valid interfaces, marked with the {@link Resource} annotation. Read the description of this annotation + * for detailed description about valid resources interfaces.
  • + *
  • {@link ResourceArray} of valid resources.
  • + *
  • {@link ResourceMap} of valid resources.
  • + *
  • {@link String}, {@link Integer}, {@link Byte}, {@link Short}, {@link Float}, {@link Double}, + * {@link Boolean}.
  • + *
  • The null value.
  • + *
+ * + *

All other types are not considered to be resources and therefore are not accepted.

* * @author Alexey Andreev */ public interface MetadataGenerator { + /** + *

Generates resources, that will be available at runtime.

+ * + * @param context context that contains useful compile-time information. + * @param method method which will be used to access the generated resources at run time. + */ Object generateMetadata(MetadataGeneratorContext context, MethodReference method); } 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 c172938d9..7003d9344 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 @@ -1,13 +1,59 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.metadata; +import java.util.Properties; import org.teavm.model.ListableClassReaderSource; +import org.teavm.vm.TeaVM; /** + *

Represents context with compile-time information, that is useful for {@link MetadataGenerator}. + * This context is provided by the compiler infrastructure.

* * @author Alexey Andreev */ public interface MetadataGeneratorContext { + /** + * Gets the collection of all classes that were achieved by the dependency checker. + */ ListableClassReaderSource getClassSource(); + /** + * Gets the class loader that is used by the compiler. + */ + ClassLoader getClassLoader(); + + /** + * Gets properties that were specified to {@link TeaVM}. + */ + Properties getProperties(); + + /** + * Creates a new resource of the given type. The description of valid resources + * is available in documentation for {@link Resource}. + */ T createResource(Class resourceType); + + /** + * Creates a new resource array. + */ + ResourceArray createResourceArray(); + + /** + * Creates a new resource map. + */ + ResourceMap createResourceMap(); } diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java index aed053db3..7065725d0 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java @@ -1,3 +1,18 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.metadata; import java.lang.annotation.ElementType; @@ -6,6 +21,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** + *

Binds a {@link MetadataGenerator} to a native method.

* * @author Alexey Andreev */ diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java index 64755a4d6..f47ac1747 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java @@ -1,3 +1,18 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.metadata; import java.lang.annotation.ElementType; @@ -6,6 +21,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** + *

Marks a valid resource interface. Resource interface is an interface, that has get* and set* methods, + * according the default convention for JavaBeans. Each property must have both getter and setter. + * Also each property's must be either primitive value, except for long or valid resource.

+ * + * @see MetadataGenerator + * @see ResourceArray + * @see ResourceMap * * @author Alexey Andreev */ diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java index 43bd6a7d0..98a85ca2a 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java @@ -1,3 +1,18 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.metadata; /** diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java index eddd2e28c..599467059 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java @@ -1,3 +1,18 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.metadata; /** 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 09f41abc6..327e6f66f 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 @@ -1,3 +1,18 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.teavm.platform.plugin; import org.teavm.vm.spi.TeaVMHost; diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java new file mode 100644 index 000000000..5260ba845 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +/** + * + * @author Alexey Andreev + */ +class ResourceProxy implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return null; + } +} From c23f62ced72c7debc7365dedfa11e3a983a33ed8 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 3 Jun 2014 17:06:56 +0400 Subject: [PATCH 03/16] Adds incomplete implementation of build-time resource proxy --- teavm-platform/pom.xml | 5 + .../org/teavm/platform/metadata/Resource.java | 2 +- .../teavm/platform/metadata/ResourceMap.java | 2 + .../plugin/BuildTimeResourceArray.java | 43 ++++ .../plugin/BuildTimeResourceGetter.java | 33 +++ .../platform/plugin/BuildTimeResourceMap.java | 43 ++++ .../plugin/BuildTimeResourceMethod.java | 24 +++ .../plugin/BuildTimeResourceProxy.java | 40 ++++ .../plugin/BuildTimeResourceProxyBuilder.java | 203 ++++++++++++++++++ .../plugin/BuildTimeResourceProxyFactory.java | 38 ++++ ...roxy.java => BuildTimeResourceSetter.java} | 16 +- .../DefaultMetadataGeneratorContext.java | 72 +++++++ .../MetadataProviderNativeGenerator.java | 43 ++++ .../plugin/MetadataProviderTransformer.java | 39 ++++ .../teavm/platform/plugin/PlatformPlugin.java | 1 + .../metadata/MetadataGeneratorTest.java | 24 +++ .../teavm/platform/metadata/TestResource.java | 31 +++ 17 files changed, 652 insertions(+), 7 deletions(-) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java rename teavm-platform/src/main/java/org/teavm/platform/plugin/{ResourceProxy.java => BuildTimeResourceSetter.java} (68%) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java create mode 100644 teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java create mode 100644 teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java diff --git a/teavm-platform/pom.xml b/teavm-platform/pom.xml index f35121baa..a4fd22179 100644 --- a/teavm-platform/pom.xml +++ b/teavm-platform/pom.xml @@ -15,5 +15,10 @@ teavm-core ${project.version} + + junit + junit + test + \ No newline at end of file diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java index f47ac1747..7b50dfe88 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java @@ -23,7 +23,7 @@ import java.lang.annotation.Target; /** *

Marks a valid resource interface. Resource interface is an interface, that has get* and set* methods, * according the default convention for JavaBeans. Each property must have both getter and setter. - * Also each property's must be either primitive value, except for long or valid resource.

+ * Also each property's must be either primitive value (except for long) or a valid resource.

* * @see MetadataGenerator * @see ResourceArray diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java index 599467059..e798f94cc 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java @@ -20,6 +20,8 @@ package org.teavm.platform.metadata; * @author Alexey Andreev */ public interface ResourceMap { + boolean has(String key); + T get(String key); void put(String key, T value); diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java new file mode 100644 index 000000000..c8aca068d --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.util.ArrayList; +import java.util.List; +import org.teavm.platform.metadata.ResourceArray; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceArray implements ResourceArray { + private List data = new ArrayList<>(); + + @Override + public int size() { + return data.size(); + } + + @Override + public T get(int i) { + return data.get(i); + } + + @Override + public void add(T elem) { + data.add(elem); + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java new file mode 100644 index 000000000..f00aa2e27 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceGetter implements BuildTimeResourceMethod { + private int index; + + public BuildTimeResourceGetter(int index) { + this.index = index; + } + + @Override + public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable { + return proxy.data[index]; + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java new file mode 100644 index 000000000..36174df46 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.util.HashMap; +import java.util.Map; +import org.teavm.platform.metadata.ResourceMap; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceMap implements ResourceMap { + private Map data = new HashMap<>(); + + @Override + public boolean has(String key) { + return data.containsKey(key); + } + + @Override + public T get(String key) { + return data.get(key); + } + + @Override + public void put(String key, T value) { + data.put(key, value); + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java new file mode 100644 index 000000000..31f3be541 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +/** + * + * @author Alexey Andreev + */ +interface BuildTimeResourceMethod { + Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable; +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java new file mode 100644 index 000000000..390acb1e5 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Map; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceProxy implements InvocationHandler { + private Map methods; + Object[] data; + + public BuildTimeResourceProxy(Map methods, Object[] initialData) { + this.methods = methods; + data = Arrays.copyOf(initialData, initialData.length); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return methods.get(method).invoke(this, args); + } +} 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 new file mode 100644 index 000000000..b5a09e757 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java @@ -0,0 +1,203 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.lang.reflect.Method; +import java.util.*; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceProxyBuilder { + private Map, BuildTimeResourceProxyFactory> factories = new HashMap<>(); + private static Set> allowedPropertyTypes = new HashSet<>(Arrays.>asList( + boolean.class, Boolean.class, byte.class, Byte.class, short.class, Short.class, + int.class, Integer.class, float.class, Float.class, double.class, Double.class, + String.class, ResourceArray.class, ResourceMap.class)); + + public BuildTimeResourceProxy buildProxy(Class iface) { + BuildTimeResourceProxyFactory factory = factories.get(iface); + if (factory == null) { + factory = createFactory(iface); + factories.put(iface, factory); + } + return factory.create(); + } + + private BuildTimeResourceProxyFactory createFactory(Class iface) { + return new ProxyFactoryCreation(iface).create(); + } + + private static class ProxyFactoryCreation { + private Class rootIface; + Map> getters = new HashMap<>(); + Map> setters = new HashMap<>(); + Map methods = new HashMap<>(); + private List initialData = new ArrayList<>(); + private Map propertyIndexes = new HashMap<>(); + + public ProxyFactoryCreation(Class iface) { + this.rootIface = iface; + } + + BuildTimeResourceProxyFactory create() { + if (!rootIface.isInterface()) { + throw new IllegalArgumentException("Error creating a new resource of type " + rootIface.getName() + + " that is not an interface"); + } + scanIface(rootIface); + return new BuildTimeResourceProxyFactory(methods, initialData.toArray(new Object[initialData.size()])); + } + + private void scanIface(Class iface) { + if (iface.isAnnotationPresent(Resource.class)) { + throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + + ". This type is not marked with the " + Resource.class.getName() + " annotation"); + } + + // Scan methods + getters.clear(); + setters.clear(); + for (Method method : iface.getDeclaredMethods()) { + if (method.getName().startsWith("get")) { + scanGetter(method); + } else if (method.getName().startsWith("is")) { + scanBooleanGetter(method); + } else if (method.getName().startsWith("set")) { + scanSetter(method); + } else { + throwInvalidMethod(method); + } + } + + // Verify consistency of getters and setters + for (Map.Entry> property : getters.entrySet()) { + String propertyName = property.getKey(); + Class getterType = property.getValue(); + Class setterType = setters.get(propertyName); + if (setterType == null) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has a getter, but does not have a setter"); + } + if (!setterType.equals(getterType)) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has a getter and a setter of different types"); + } + } + for (String propertyName : setters.keySet()) { + if (!getters.containsKey(propertyName)) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has a setter, but does not have a getter"); + } + } + + // Verify types of properties and fill default values + for (Map.Entry> property : getters.entrySet()) { + String propertyName = property.getKey(); + Class propertyType = property.getValue(); + if (allowedPropertyTypes.contains(propertyType)) { + continue; + } + if (!propertyType.isInterface() || !propertyType.isAnnotationPresent(Resource.class)) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has an illegal type " + propertyType.getName()); + } + } + // TODO: fill default values + + // Scan superinterfaces + for (Class superIface : iface.getInterfaces()) { + scanIface(superIface); + } + } + + private void throwInvalidMethod(Method method) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is not likely to be either getter or setter"); + } + + private void scanGetter(Method method) { + String propertyName = extractPropertyName(method.getName().substring(3)); + if (propertyName == null || method.getReturnType().equals(void.class) || + method.getParameterTypes().length > 0) { + throwInvalidMethod(method); + } + if (getters.put(propertyName, method.getReturnType()) != null) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is a duplicate getter for property " + propertyName); + } + methods.put(method, new BuildTimeResourceGetter(getPropertyIndex(propertyName))); + } + + private void scanBooleanGetter(Method method) { + String propertyName = extractPropertyName(method.getName().substring(2)); + if (propertyName == null || !method.getReturnType().equals(boolean.class) || + method.getParameterTypes().length > 0) { + throwInvalidMethod(method); + } + if (getters.put(propertyName, method.getReturnType()) != null) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is a duplicate getter for property " + propertyName); + } + methods.put(method, new BuildTimeResourceGetter(getPropertyIndex(propertyName))); + } + + private void scanSetter(Method method) { + String propertyName = extractPropertyName(method.getName().substring(2)); + if (propertyName == null || !method.getReturnType().equals(void.class) || + method.getParameterTypes().length != 1) { + throwInvalidMethod(method); + } + if (setters.put(propertyName, method.getParameterTypes()[0]) != null) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is a duplicate setter for property " + propertyName); + } + methods.put(method, new BuildTimeResourceSetter(getPropertyIndex(propertyName))); + } + + private String extractPropertyName(String propertyName) { + if (propertyName.isEmpty()) { + return null; + } + char c = propertyName.charAt(0); + if (c != Character.toUpperCase(c)) { + return null; + } + if (propertyName.length() == 1) { + return propertyName.toLowerCase(); + } + c = propertyName.charAt(1); + if (c == Character.toUpperCase(c)) { + return propertyName; + } else { + return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1); + } + } + + private int getPropertyIndex(String propertyName) { + Integer index = propertyIndexes.get(propertyName); + if (index == null) { + index = propertyIndexes.size(); + propertyIndexes.put(propertyName, index); + } + return index; + } + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java new file mode 100644 index 000000000..4283bda22 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceProxyFactory { + private Map methods = new HashMap<>(); + private Object[] initialData; + + public BuildTimeResourceProxyFactory(Map methods, Object[] initialData) { + this.methods = methods; + this.initialData = initialData; + } + + BuildTimeResourceProxy create() { + return new BuildTimeResourceProxy(methods, initialData); + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java similarity index 68% rename from teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java rename to teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java index 5260ba845..e8caaa40d 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProxy.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java @@ -15,16 +15,20 @@ */ package org.teavm.platform.plugin; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - /** * - * @author Alexey Andreev + * @author Alexey Andreev */ -class ResourceProxy implements InvocationHandler { +class BuildTimeResourceSetter implements BuildTimeResourceMethod { + private int index; + + public BuildTimeResourceSetter(int index) { + this.index = index; + } + @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable { + proxy.data[index] = args[0]; return null; } } 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 new file mode 100644 index 000000000..608f7fbfe --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.lang.reflect.Proxy; +import java.util.Properties; +import org.teavm.model.ListableClassReaderSource; +import org.teavm.platform.metadata.MetadataGeneratorContext; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; + +/** + * + * @author Alexey Andreev + */ +class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { + private ListableClassReaderSource classSource; + private ClassLoader classLoader; + private Properties properties; + private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder(); + + private DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, + Properties properties) { + this.classSource = classSource; + this.classLoader = classLoader; + this.properties = properties; + } + + @Override + public ListableClassReaderSource getClassSource() { + return classSource; + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public Properties getProperties() { + return new Properties(properties); + } + + @Override + public T createResource(Class resourceType) { + return resourceType.cast(Proxy.newProxyInstance(classLoader, new Class[] { resourceType }, + proxyBuilder.buildProxy(resourceType))); + } + + @Override + public ResourceArray createResourceArray() { + return new BuildTimeResourceArray<>(); + } + + @Override + public ResourceMap createResourceMap() { + return new BuildTimeResourceMap<>(); + } +} 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 new file mode 100644 index 000000000..9d9c2a8dd --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.io.IOException; +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.*; +import org.teavm.platform.metadata.MetadataProvider; + +/** + * + * @author Alexey Andreev + */ +class MetadataProviderNativeGenerator implements Generator { + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + ClassReader cls = context.getClassSource().get(methodRef.getClassName()); + MethodReader method = cls.getMethod(methodRef.getDescriptor()); + AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); + if (providerAnnot == null) { + return; + } + if (!method.hasModifier(ElementModifier.NATIVE)) { + throw new IllegalStateException("Method " + method.getReference() + " was marked with " + + MetadataProvider.class.getName() + " but it is not native"); + } + } +} 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 new file mode 100644 index 000000000..703052af3 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import org.teavm.javascript.ni.GeneratedBy; +import org.teavm.model.*; +import org.teavm.platform.metadata.MetadataProvider; + +/** + * + * @author Alexey Andreev + */ +class MetadataProviderTransformer implements ClassHolderTransformer { + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { + for (MethodHolder method : cls.getMethods()) { + AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); + if (providerAnnot == null) { + return; + } + AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName()); + genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(null))); + method.getAnnotations().add(genAnnot); + } + } +} 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 327e6f66f..6589f4c8b 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 @@ -25,5 +25,6 @@ import org.teavm.vm.spi.TeaVMPlugin; public class PlatformPlugin implements TeaVMPlugin { @Override public void install(TeaVMHost host) { + host.add(new MetadataProviderTransformer()); } } diff --git a/teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java new file mode 100644 index 000000000..4f240dd36 --- /dev/null +++ b/teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public class MetadataGeneratorTest { + +} diff --git a/teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java b/teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java new file mode 100644 index 000000000..d75474deb --- /dev/null +++ b/teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +@Resource +public interface TestResource { + int getInt(); + + void setInt(int value); + + String getString(); + + void setString(String string); +} From 10a8e0261a144cf63788b5870271fba5a51e80dc Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Wed, 4 Jun 2014 16:30:42 +0400 Subject: [PATCH 04/16] Continues implementation of metadata providers --- .../java/org/teavm/codegen/SourceWriter.java | 57 +++++++++++--- .../teavm/codegen/SourceWriterBuilder.java | 7 +- .../java/org/teavm/javascript/Renderer.java | 21 ++++++ .../teavm/javascript/RenderingContext.java | 3 + .../teavm/javascript/ni/GeneratorContext.java | 5 ++ .../plugin/BuildTimeResourceArray.java | 16 +++- .../platform/plugin/BuildTimeResourceMap.java | 20 ++++- .../plugin/BuildTimeResourceProxyBuilder.java | 28 ++++--- .../plugin/BuildTimeResourceWriterMethod.java | 46 ++++++++++++ .../DefaultMetadataGeneratorContext.java | 5 +- .../MetadataProviderNativeGenerator.java | 44 ++++++++++- .../teavm/platform/plugin/ResourceWriter.java | 27 +++++++ .../platform/plugin/ResourceWriterHelper.java | 74 +++++++++++++++++++ 13 files changed, 328 insertions(+), 25 deletions(-) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java index 8551956cd..1ca23eed4 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java @@ -31,10 +31,13 @@ public class SourceWriter implements Appendable { private NamingStrategy naming; private boolean lineStart; private boolean minified; + private int lineWidth; + private int pos; - SourceWriter(NamingStrategy naming, Appendable innerWriter) { + SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) { this.naming = naming; this.innerWriter = innerWriter; + this.lineWidth = lineWidth; } void setMinified(boolean minified) { @@ -42,8 +45,7 @@ public class SourceWriter implements Appendable { } public SourceWriter append(String value) throws IOException { - appendIndent(); - innerWriter.append(value); + append((CharSequence)value); return this; } @@ -59,23 +61,43 @@ public class SourceWriter implements Appendable { public SourceWriter append(char value) throws IOException { appendIndent(); innerWriter.append(value); + if (value == '\n') { + newLine(); + } else { + pos++; + } return this; } @Override public SourceWriter append(CharSequence csq) throws IOException { - appendIndent(); - innerWriter.append(csq); + append(csq, 0, csq.length()); return this; } @Override public SourceWriter append(CharSequence csq, int start, int end) throws IOException { - appendIndent(); - innerWriter.append(csq, start, end); + int last = start; + for (int i = start; i < end; ++i) { + if (csq.charAt(i) == '\n') { + appendSingleLine(csq, last, i); + newLine(); + last = i; + } + } + appendSingleLine(csq, last, end); return this; } + private void appendSingleLine(CharSequence csq, int start, int end) throws IOException { + if (start == end) { + return; + } + appendIndent(); + pos += end - start; + innerWriter.append(csq, start, end); + } + public SourceWriter appendClass(String cls) throws NamingException, IOException { return append(naming.getNameFor(cls)); } @@ -109,6 +131,7 @@ public class SourceWriter implements Appendable { if (lineStart) { for (int i = 0; i < indentSize; ++i) { innerWriter.append(" "); + pos += 4; } lineStart = false; } @@ -116,13 +139,26 @@ public class SourceWriter implements Appendable { public SourceWriter newLine() throws IOException{ innerWriter.append('\n'); + pos = 0; lineStart = true; return this; } - public SourceWriter ws() throws IOException{ - if (!minified) { - innerWriter.append(' '); + public SourceWriter ws() throws IOException { + if (pos >= lineWidth) { + newLine(); + } else { + if (!minified) { + innerWriter.append(' '); + pos++; + } + } + return this; + } + + public SourceWriter tokenBoundary() throws IOException { + if (pos >= lineWidth) { + newLine(); } return this; } @@ -130,6 +166,7 @@ public class SourceWriter implements Appendable { public SourceWriter softNewLine() throws IOException{ if (!minified) { innerWriter.append('\n'); + pos = 0; lineStart = true; } return this; diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java index f3748421c..35796b388 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java @@ -22,6 +22,7 @@ package org.teavm.codegen; public class SourceWriterBuilder { private NamingStrategy naming; private boolean minified; + private int lineWidth = 512; public SourceWriterBuilder(NamingStrategy naming) { this.naming = naming; @@ -35,8 +36,12 @@ public class SourceWriterBuilder { this.minified = minified; } + public void setLineWidth(int lineWidth) { + this.lineWidth = lineWidth; + } + public SourceWriter build(Appendable innerWriter) { - SourceWriter writer = new SourceWriter(naming, innerWriter); + SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth); writer.setMinified(minified); return writer; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 8fef2ed31..dbb2986b8 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -43,6 +43,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext private ClassLoader classLoader; private boolean minifying; private Map injectorMap = new HashMap<>(); + private Properties properties = new Properties(); private static class InjectorHolder { public final Injector injector; @@ -88,6 +89,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext return classLoader; } + @Override + public Properties getProperties() { + return new Properties(properties); + } + + public void setProperties(Properties properties) { + this.properties.clear(); + this.properties.putAll(properties); + } + public void renderRuntime() throws RenderingException { try { renderRuntimeCls(); @@ -483,6 +494,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext public ListableClassReaderSource getClassSource() { return classSource; } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public Properties getProperties() { + return null; + } } @Override diff --git a/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java b/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java index 2b15f5684..48e2b6cae 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java @@ -15,6 +15,7 @@ */ package org.teavm.javascript; +import java.util.Properties; import org.teavm.codegen.NamingStrategy; import org.teavm.codegen.SourceWriter; import org.teavm.model.ListableClassReaderSource; @@ -33,4 +34,6 @@ public interface RenderingContext { ListableClassReaderSource getClassSource(); ClassLoader getClassLoader(); + + Properties getProperties(); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java index 9082b8819..d54363239 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java @@ -15,6 +15,7 @@ */ package org.teavm.javascript.ni; +import java.util.Properties; import org.teavm.model.ListableClassReaderSource; /** @@ -25,4 +26,8 @@ public interface GeneratorContext { String getParameterName(int index); ListableClassReaderSource getClassSource(); + + ClassLoader getClassLoader(); + + Properties getProperties(); } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java index c8aca068d..43d1ae312 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java @@ -15,15 +15,17 @@ */ package org.teavm.platform.plugin; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.teavm.codegen.SourceWriter; import org.teavm.platform.metadata.ResourceArray; /** * * @author Alexey Andreev */ -class BuildTimeResourceArray implements ResourceArray { +class BuildTimeResourceArray implements ResourceArray, ResourceWriter { private List data = new ArrayList<>(); @Override @@ -40,4 +42,16 @@ class BuildTimeResourceArray implements ResourceArray { public void add(T elem) { data.add(elem); } + + @Override + public void write(SourceWriter writer) throws IOException { + writer.append('[').tokenBoundary(); + for (int i = 0; i < data.size(); ++i) { + if (i > 0) { + writer.append(',').ws(); + } + ResourceWriterHelper.write(writer, data.get(i)); + } + writer.append(']').tokenBoundary(); + } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java index 36174df46..213b2c479 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java @@ -15,15 +15,17 @@ */ package org.teavm.platform.plugin; +import java.io.IOException; import java.util.HashMap; import java.util.Map; +import org.teavm.codegen.SourceWriter; import org.teavm.platform.metadata.ResourceMap; /** * * @author Alexey Andreev */ -class BuildTimeResourceMap implements ResourceMap { +class BuildTimeResourceMap implements ResourceMap, ResourceWriter { private Map data = new HashMap<>(); @Override @@ -40,4 +42,20 @@ class BuildTimeResourceMap implements ResourceMap { public void put(String key, T value) { data.put(key, value); } + + @Override + public void write(SourceWriter writer) throws IOException { + writer.append('{'); + boolean first = true; + for (Map.Entry entry : data.entrySet()) { + if (!first) { + writer.append(",").ws(); + } + first = false; + ResourceWriterHelper.writeString(writer, entry.getKey()); + writer.ws().append(':').ws(); + ResourceWriterHelper.write(writer, entry.getValue()); + } + writer.append('}').tokenBoundary(); + } } 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 b5a09e757..0baf44b47 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 @@ -31,6 +31,16 @@ class BuildTimeResourceProxyBuilder { boolean.class, Boolean.class, byte.class, Byte.class, short.class, Short.class, int.class, Integer.class, float.class, Float.class, double.class, Double.class, String.class, ResourceArray.class, ResourceMap.class)); + private static Map, Object> defaultValues = new HashMap<>(); + + static { + defaultValues.put(boolean.class, false); + defaultValues.put(byte.class, (byte)0); + defaultValues.put(short.class, (short)0); + defaultValues.put(int.class, 0); + defaultValues.put(float.class, 0F); + defaultValues.put(double.class, 0.0); + } public BuildTimeResourceProxy buildProxy(Class iface) { BuildTimeResourceProxyFactory factory = factories.get(iface); @@ -50,8 +60,8 @@ class BuildTimeResourceProxyBuilder { Map> getters = new HashMap<>(); Map> setters = new HashMap<>(); Map methods = new HashMap<>(); - private List initialData = new ArrayList<>(); private Map propertyIndexes = new HashMap<>(); + private Object[] initialData; public ProxyFactoryCreation(Class iface) { this.rootIface = iface; @@ -63,7 +73,7 @@ class BuildTimeResourceProxyBuilder { " that is not an interface"); } scanIface(rootIface); - return new BuildTimeResourceProxyFactory(methods, initialData.toArray(new Object[initialData.size()])); + return new BuildTimeResourceProxyFactory(methods, initialData); } private void scanIface(Class iface) { @@ -109,18 +119,18 @@ class BuildTimeResourceProxyBuilder { } // Verify types of properties and fill default values + initialData = new Object[propertyIndexes.size()]; for (Map.Entry> property : getters.entrySet()) { String propertyName = property.getKey(); Class propertyType = property.getValue(); - if (allowedPropertyTypes.contains(propertyType)) { - continue; - } - if (!propertyType.isInterface() || !propertyType.isAnnotationPresent(Resource.class)) { - throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + - " has an illegal type " + propertyType.getName()); + if (!allowedPropertyTypes.contains(propertyType)) { + if (!propertyType.isInterface() || !propertyType.isAnnotationPresent(Resource.class)) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has an illegal type " + propertyType.getName()); + } } + initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType); } - // TODO: fill default values // Scan superinterfaces for (Class superIface : iface.getInterfaces()) { 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 new file mode 100644 index 000000000..dead2901c --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import org.teavm.codegen.SourceWriter; + +/** + * + * @author Alexey Andreev + */ +class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod { + private String[] propertyNames; + + public BuildTimeResourceWriterMethod(String[] propertyNames) { + this.propertyNames = propertyNames; + } + + @Override + public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable { + SourceWriter writer = (SourceWriter)args[0]; + writer.append('{'); + for (int i = 0; i < propertyNames.length; ++i) { + if (i > 0) { + writer.append(',').ws(); + } + ResourceWriterHelper.writeString(writer, propertyNames[i]); + writer.ws().append(':').ws(); + ResourceWriterHelper.write(writer, proxy.data[i]); + } + writer.append('}').tokenBoundary(); + return null; + } +} 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 608f7fbfe..cd38cc840 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 @@ -32,7 +32,7 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { private Properties properties; private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder(); - private DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, + public DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, Properties properties) { this.classSource = classSource; this.classLoader = classLoader; @@ -56,7 +56,8 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { @Override public T createResource(Class resourceType) { - return resourceType.cast(Proxy.newProxyInstance(classLoader, new Class[] { resourceType }, + return resourceType.cast(Proxy.newProxyInstance(classLoader, + new Class[] { resourceType, ResourceWriter.class }, proxyBuilder.buildProxy(resourceType))); } 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 9d9c2a8dd..2d43695e4 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 @@ -16,19 +16,23 @@ package org.teavm.platform.plugin; 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.model.*; +import org.teavm.platform.metadata.MetadataGenerator; import org.teavm.platform.metadata.MetadataProvider; /** * * @author Alexey Andreev */ -class MetadataProviderNativeGenerator implements Generator { +public class MetadataProviderNativeGenerator 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(MetadataProvider.class.getName()); @@ -39,5 +43,43 @@ class MetadataProviderNativeGenerator implements Generator { throw new IllegalStateException("Method " + method.getReference() + " was marked with " + MetadataProvider.class.getName() + " but it is not native"); } + + // 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) { + throw new RuntimeException("Can't find metadata generator class: " + generatorClassName, e); + } + Constructor cons; + try { + cons = generatorClass.getConstructor(); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Metadata generator " + generatorClassName + " does not have a public " + + "no-arg constructor", e); + } + 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()); + } + DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(), + context.getClassLoader(), context.getProperties()); + + // Generate resource loader + Object resource = generator.generateMetadata(metadataContext, methodRef); + writer.append("if (!").appendMethodBody(methodRef).append("$$resource === undefined").append(") {") + .indent().softNewLine(); + writer.appendMethodBody(methodRef).append("$$resource = "); + ResourceWriterHelper.write(writer, resource); + writer.append(';').softNewLine(); + writer.outdent().append('}').softNewLine(); + writer.append("return ").appendMethodBody(methodRef).append("$$resource;").softNewLine(); } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java new file mode 100644 index 000000000..5f1be5689 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.io.IOException; +import org.teavm.codegen.SourceWriter; + +/** + * + * @author Alexey Andreev + */ +interface ResourceWriter { + void write(SourceWriter writer) throws IOException; +} 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 new file mode 100644 index 000000000..b263a3f24 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import java.io.IOException; +import org.teavm.codegen.SourceWriter; + +/** + * + * @author Alexey Andreev + */ +final class ResourceWriterHelper { + public static void write(SourceWriter writer, Object resource) throws IOException { + if (resource == null) { + writer.append("null"); + } else { + if (resource instanceof ResourceWriter) { + ((ResourceWriter)resource).write(writer); + } else if (resource instanceof Number) { + writer.append(resource); + } else if (resource instanceof Boolean) { + writer.append(resource == Boolean.TRUE ? "true" : "false"); + } else if (resource instanceof String) { + writeString(writer, (String)resource); + } else { + throw new RuntimeException("Error compiling resources. Value of illegal type found: " + + resource.getClass()); + } + } + } + + public static void writeString(SourceWriter writer, String s) throws IOException { + writer.append('"'); + for (int i = 0; i < s.length(); ++i) { + char c = s.charAt(i); + switch (c) { + case '\0': + writer.append("\\0"); + break; + case '\n': + writer.append("\\n"); + break; + case '\r': + writer.append("\\r"); + break; + case '\t': + writer.append("\\t"); + break; + default: + if (c < 32) { + writer.append("\\u00").append(Character.forDigit(c / 16, 16)) + .append(Character.forDigit(c % 16, 16)); + } else { + writer.append(c); + } + break; + } + } + writer.append('"'); + } +} From 3cf7991cfb17b86c9ebdbc529eed94525e973e6e Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Thu, 5 Jun 2014 15:18:40 +0400 Subject: [PATCH 05/16] Adds ResourceAccessor and its implementation. ResourceAccessor is to access properties and methods of resource in rumtime. --- .../java/org/teavm/javascript/Decompiler.java | 12 +- .../java/org/teavm/javascript/Renderer.java | 4 + .../org/teavm/model/MethodDescriptor.java | 11 + .../java/org/teavm/model/MethodReference.java | 4 + .../main/java/org/teavm/model/ValueType.java | 26 +++ .../src/main/java/org/teavm/vm/TeaVM.java | 17 +- .../main/java/org/teavm/vm/spi/TeaVMHost.java | 3 + .../org/teavm/platform/metadata/Resource.java | 7 +- .../teavm/platform/plugin/PlatformPlugin.java | 3 + .../platform/plugin/ResourceAccessor.java | 86 ++++++++ .../ResourceAccessorDependencyListener.java | 95 +++++++++ .../plugin/ResourceAccessorGenerator.java | 193 ++++++++++++++++++ .../plugin/ResourceAccessorTransformer.java | 44 ++++ .../platform/plugin/ResourceTransformer.java | 30 +++ 14 files changed, 527 insertions(+), 8 deletions(-) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index 6630b8362..72f45cefc 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -44,6 +44,7 @@ public class Decompiler { private RangeTree.Node parentNode; private FiniteExecutor executor; private Map generators = new HashMap<>(); + private Set methodsToPass = new HashSet<>(); public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) { this.classSource = classSource; @@ -96,6 +97,10 @@ public class Decompiler { generators.put(method, generator); } + public void addMethodToPass(MethodReference method) { + methodsToPass.add(method); + } + private void orderClasses(String className, Set visited, List order) { if (!visited.add(className)) { return; @@ -125,7 +130,8 @@ public class Decompiler { if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { continue; } - if (method.getAnnotations().get(InjectedBy.class.getName()) != null) { + if (method.getAnnotations().get(InjectedBy.class.getName()) != null || + methodsToPass.contains(method)) { continue; } MethodNode methodNode = decompile(method); @@ -140,8 +146,8 @@ public class Decompiler { } public MethodNode decompile(MethodHolder method) { - return method.getModifiers().contains(ElementModifier.NATIVE) ? - decompileNative(method) : decompileRegular(method); + return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) : + decompileRegular(method); } public NativeMethodNode decompileNative(MethodHolder method) { diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index dbb2986b8..1006a503b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -53,6 +53,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } } + public void addInjector(MethodReference method, Injector injector) { + injectorMap.put(method, new InjectorHolder(injector)); + } + public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader) { this.naming = writer.getNaming(); this.writer = writer; diff --git a/teavm-core/src/main/java/org/teavm/model/MethodDescriptor.java b/teavm-core/src/main/java/org/teavm/model/MethodDescriptor.java index 4bcc64a4b..aeba3a45e 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodDescriptor.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodDescriptor.java @@ -34,6 +34,17 @@ public class MethodDescriptor { this.signature = Arrays.copyOf(signature, signature.length); } + public MethodDescriptor(String name, Class... signature) { + if (signature.length < 1) { + throw new IllegalArgumentException("Signature must be at least 1 element length"); + } + this.name = name; + this.signature = new ValueType[signature.length]; + for (int i = 0; i < signature.length; ++i) { + this.signature[i] = ValueType.parse(signature[i]); + } + } + public String getName() { return name; } diff --git a/teavm-core/src/main/java/org/teavm/model/MethodReference.java b/teavm-core/src/main/java/org/teavm/model/MethodReference.java index 3f5ad5da4..38979ac56 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodReference.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodReference.java @@ -55,6 +55,10 @@ public class MethodReference { this(className, new MethodDescriptor(name, signature)); } + public MethodReference(Class cls, String name, Class... signature) { + this(cls.getName(), new MethodDescriptor(name, signature)); + } + public String getClassName() { return className; } diff --git a/teavm-core/src/main/java/org/teavm/model/ValueType.java b/teavm-core/src/main/java/org/teavm/model/ValueType.java index 86d0b9a13..b4f7795ab 100644 --- a/teavm-core/src/main/java/org/teavm/model/ValueType.java +++ b/teavm-core/src/main/java/org/teavm/model/ValueType.java @@ -16,7 +16,9 @@ package org.teavm.model; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** @@ -25,6 +27,7 @@ import java.util.List; */ public abstract class ValueType { volatile String reprCache; + private static final Map, ValueType> primitiveMap = new HashMap<>(); private ValueType() { } @@ -166,6 +169,19 @@ public abstract class ValueType { public static final Null NULL = new Null(); + + static { + primitiveMap.put(boolean.class, BOOLEAN); + primitiveMap.put(char.class, CHARACTER); + primitiveMap.put(byte.class, BYTE); + primitiveMap.put(short.class, SHORT); + primitiveMap.put(int.class, INTEGER); + primitiveMap.put(long.class, LONG); + primitiveMap.put(float.class, FLOAT); + primitiveMap.put(double.class, DOUBLE); + primitiveMap.put(void.class, VOID); + } + public static ValueType object(String cls) { return new Object(cls); } @@ -267,6 +283,16 @@ public abstract class ValueType { } } + public static ValueType parse(Class cls) { + if (cls.isPrimitive()) { + return primitiveMap.get(cls); + } else if (cls.getComponentType() != null) { + return ValueType.arrayOf(ValueType.parse(cls.getComponentType())); + } else { + return ValueType.object(cls.getName()); + } + } + @Override public int hashCode() { return toString().hashCode(); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 66cc8ee43..8eba7ff75 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -20,9 +20,12 @@ import java.util.*; import org.teavm.codegen.*; import org.teavm.common.FiniteExecutor; import org.teavm.dependency.*; -import org.teavm.javascript.*; +import org.teavm.javascript.Decompiler; +import org.teavm.javascript.Renderer; +import org.teavm.javascript.RenderingException; import org.teavm.javascript.ast.ClassNode; import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.Injector; import org.teavm.model.*; import org.teavm.model.util.ListingBuilder; import org.teavm.model.util.ProgramUtils; @@ -73,6 +76,7 @@ public class TeaVM implements TeaVMHost { private Map entryPoints = new HashMap<>(); private Map exportedClasses = new HashMap<>(); private Map methodGenerators = new HashMap<>(); + private Map methodInjectors = new HashMap<>(); private List rendererListeners = new ArrayList<>(); private Properties properties = new Properties(); @@ -98,6 +102,11 @@ public class TeaVM implements TeaVMHost { methodGenerators.put(methodRef, generator); } + @Override + public void add(MethodReference methodRef, Injector injector) { + methodInjectors.put(methodRef, injector); + } + @Override public void add(RendererListener listener) { rendererListeners.add(listener); @@ -315,6 +324,9 @@ public class TeaVM implements TeaVMHost { for (Map.Entry entry : methodGenerators.entrySet()) { decompiler.addGenerator(entry.getKey(), entry.getValue()); } + for (MethodReference injectedMethod : methodInjectors.keySet()) { + decompiler.addMethodToPass(injectedMethod); + } List clsNodes = decompiler.decompile(classSet.getClassNames()); // Render @@ -324,6 +336,9 @@ public class TeaVM implements TeaVMHost { builder.setMinified(minifying); SourceWriter sourceWriter = builder.build(writer); Renderer renderer = new Renderer(sourceWriter, classSet, classLoader); + for (Map.Entry entry : methodInjectors.entrySet()) { + renderer.addInjector(entry.getKey(), entry.getValue()); + } try { for (RendererListener listener : rendererListeners) { listener.begin(renderer, target); diff --git a/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java b/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java index fc0f2cfbc..2acbda0af 100644 --- a/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java +++ b/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java @@ -18,6 +18,7 @@ package org.teavm.vm.spi; import java.util.Properties; import org.teavm.dependency.DependencyListener; import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.Injector; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.MethodReference; import org.teavm.vm.TeaVM; @@ -36,6 +37,8 @@ public interface TeaVMHost { void add(MethodReference methodRef, Generator generator); + void add(MethodReference methodRef, Injector injector); + void add(RendererListener listener); /** diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java index 7b50dfe88..3524b52d9 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java @@ -15,10 +15,7 @@ */ 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; +import java.lang.annotation.*; /** *

Marks a valid resource interface. Resource interface is an interface, that has get* and set* methods, @@ -33,5 +30,7 @@ import java.lang.annotation.Target; */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@Inherited +@Documented public @interface Resource { } 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 6589f4c8b..ad16bfc25 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 @@ -26,5 +26,8 @@ public class PlatformPlugin implements TeaVMPlugin { @Override public void install(TeaVMHost host) { host.add(new MetadataProviderTransformer()); + host.add(new ResourceTransformer()); + host.add(new ResourceAccessorTransformer(host)); + host.add(new ResourceAccessorDependencyListener()); } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java new file mode 100644 index 000000000..e91ca89c2 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java @@ -0,0 +1,86 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +/** + * + * @author Alexey Andreev + */ +final class ResourceAccessor { + public static native Object get(Object obj, String propertyName); + + public static native void put(Object obj, String propertyName, Object elem); + + public static native Object get(Object obj, int index); + + public static native Object add(Object obj, Object elem); + + public static native boolean has(Object obj, String key); + + public static native boolean size(Object obj); + + public static native int castToInt(Object obj); + + public static native Integer castToIntWrapper(Object obj); + + public static native short castToShort(Object obj); + + public static native Short castToShortWrapper(Object obj); + + public static native byte castToByte(Object obj); + + public static native Byte castToByteWrapper(Object obj); + + public static native boolean castToBoolean(Object obj); + + public static native Boolean castToBooleanWrapper(Object obj); + + public static native float castToFloat(Object obj); + + public static native Float castToFloatWrapper(Object obj); + + public static native double castToDouble(Object obj); + + public static native Double castToDoubleWrapper(Object obj); + + public static native String castToString(Object obj); + + public static native Object castFromInt(int value); + + public static native Object castFromIntWrapper(Integer value); + + public static native Object castFromShort(short value); + + public static native Object castFromShortWrapper(Short value); + + public static native Object castFromByte(byte value); + + public static native Object castFromByteWrapper(Byte value); + + public static native Object castFromBoolean(boolean value); + + public static native Object castFromBooleanWrapper(Boolean value); + + public static native Object castFromFloat(float value); + + public static native Object castFromFloatWrapper(Float value); + + public static native Object castFromDouble(double value); + + public static native Object castFromDoubleWrapper(Double value); + + public static native Object castFromString(String value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java new file mode 100644 index 000000000..98345dbd1 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyListener; +import org.teavm.dependency.FieldDependency; +import org.teavm.dependency.MethodDependency; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +class ResourceAccessorDependencyListener implements DependencyListener { + @Override + public void started(DependencyAgent agent) { + } + + @Override + public void classAchieved(DependencyAgent agent, String className) { + } + + @Override + public void methodAchieved(DependencyAgent agent, MethodDependency method) { + switch (method.getReference().getName()) { + case "castToIntWrapper": + castToWrapper(agent, method, Integer.class, int.class); + break; + case "castToShortWrapper": + castToWrapper(agent, method, Short.class, short.class); + break; + case "castToByteWrapper": + castToWrapper(agent, method, Byte.class, byte.class); + break; + case "castToBooleanWrapper": + castToWrapper(agent, method, Boolean.class, boolean.class); + break; + case "castToFloatWrapper": + castToWrapper(agent, method, Float.class, float.class); + break; + case "castToDoubleWrapper": + castToWrapper(agent, method, Double.class, double.class); + break; + case "castFromIntegerWrapper": + castFromWrapper(agent, method, Integer.class, int.class); + break; + case "castFromShortWrapper": + castFromWrapper(agent, method, Short.class, short.class); + break; + case "castFromByteWrapper": + castFromWrapper(agent, method, Byte.class, byte.class); + break; + case "castFromBooleanWrapper": + castFromWrapper(agent, method, Boolean.class, boolean.class); + break; + case "castFromFloatWrapper": + castFromWrapper(agent, method, Float.class, float.class); + break; + case "castFromDoubleWrapper": + castFromWrapper(agent, method, Double.class, double.class); + break; + case "castToString": + method.getResult().propagate("java.lang.String"); + break; + } + } + + private void castToWrapper(DependencyAgent agent, MethodDependency method, Class wrapper, Class primitive) { + method.getResult().propagate(wrapper.getName()); + agent.linkMethod(new MethodReference(wrapper, "valueOf", primitive, wrapper), method.getStack()).use(); + } + + private void castFromWrapper(DependencyAgent agent, MethodDependency method, Class wrapper, Class primitive) { + String primitiveName = primitive.getName(); + agent.linkMethod(new MethodReference(wrapper, primitiveName + "Value", primitive), method.getStack()).use(); + } + + @Override + public void fieldAchieved(DependencyAgent agent, FieldDependency field) { + } +} 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 new file mode 100644 index 000000000..5e8d7542d --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java @@ -0,0 +1,193 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.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.model.MethodReference; +import org.teavm.model.ValueType; + +/** + * + * @author Alexey Andreev + */ +class ResourceAccessorGenerator implements Injector { + @Override + public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "get": + if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) { + context.writeExpr(context.getArgument(1)); + context.getWriter().append('['); + context.writeExpr(context.getArgument(2)); + context.getWriter().append(']'); + } else { + context.writeExpr(context.getArgument(1)); + writePropertyAccessor(context, context.getArgument(2)); + } + break; + case "put": + context.getWriter().append('('); + if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) { + context.writeExpr(context.getArgument(1)); + context.getWriter().append('['); + context.writeExpr(context.getArgument(2)); + } else { + context.writeExpr(context.getArgument(1)); + writePropertyAccessor(context, context.getArgument(2)); + } + context.getWriter().append(']').ws().append('=').ws(); + context.writeExpr(context.getArgument(3)); + context.getWriter().append(')'); + break; + case "add": + context.writeExpr(context.getArgument(1)); + context.getWriter().append(".push("); + context.writeExpr(context.getArgument(2)); + context.getWriter().append(')'); + break; + case "has": + context.writeExpr(context.getArgument(1)); + context.getWriter().append(".hasOwnProperty("); + writeStringExpr(context, context.getArgument(2)); + context.getWriter().append(')'); + break; + case "size": + context.writeExpr(context.getArgument(1)); + context.getWriter().append(".length"); + break; + case "castToInt": + case "castToShort": + case "castToByte": + case "castToBoolean": + case "castToFloat": + case "castToDouble": + case "castFromInt": + case "castFromShort": + case "castFromByte": + case "castFromBoolean": + case "castFromFloat": + case "castFromDouble": + context.writeExpr(context.getArgument(1)); + break; + case "castToIntWrapper": + castToWrapper(context, Integer.class, int.class); + break; + case "castToShortWrapper": + castToWrapper(context, Short.class, short.class); + break; + case "castToByteWrapper": + castToWrapper(context, Byte.class, byte.class); + break; + case "castToBooleanWrapper": + castToWrapper(context, Boolean.class, boolean.class); + break; + case "castToFloatWrapper": + castToWrapper(context, Float.class, float.class); + break; + case "castToDoubleWrapper": + castToWrapper(context, Double.class, double.class); + break; + case "castFromIntWrapper": + castFromWrapper(context, Integer.class, int.class); + break; + case "castFromShortWrapper": + castFromWrapper(context, Short.class, short.class); + break; + case "castFromByteWrapper": + castFromWrapper(context, Byte.class, byte.class); + break; + case "castFromBooleanWrapper": + castFromWrapper(context, Boolean.class, boolean.class); + break; + case "castFromFloatWrapper": + castFromWrapper(context, Float.class, float.class); + break; + case "castFromDoubleWrapper": + castFromWrapper(context, Double.class, double.class); + break; + case "castToString": + context.getWriter().append("$rt_str("); + context.writeExpr(context.getArgument(1)); + context.getWriter().append(")"); + break; + case "castFromString": + context.getWriter().append("$rt_ustr("); + context.writeExpr(context.getArgument(1)); + context.getWriter().append(")"); + break; + } + } + + private void castToWrapper(InjectorContext context, Class wrapper, Class primitive) throws IOException { + context.getWriter().appendMethodBody(new MethodReference(wrapper, "valueOf", primitive, wrapper)).append('('); + context.writeExpr(context.getArgument(1)); + context.getWriter().append(')'); + } + + private void castFromWrapper(InjectorContext context, Class wrapper, Class primitive) throws IOException { + String primitiveName = primitive.getName(); + context.getWriter().appendMethodBody(new MethodReference(wrapper, primitiveName + "Value", primitive)) + .append('('); + context.writeExpr(context.getArgument(1)); + context.getWriter().append(')'); + } + + private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException { + if (property instanceof ConstantExpr) { + String str = (String)((ConstantExpr)property).getValue(); + if (str.isEmpty()) { + context.getWriter().append("[\"\"]"); + return; + } + if (isValidIndentifier(str)) { + context.getWriter().append(".").append(str); + return; + } + } + context.getWriter().append("[$rt_ustr("); + context.writeExpr(property); + context.getWriter().append(")]"); + } + + private void writeStringExpr(InjectorContext context, Expr expr) throws IOException { + if (expr instanceof ConstantExpr) { + String str = (String)((ConstantExpr)expr).getValue(); + context.getWriter().append('"'); + context.writeEscaped(str); + context.getWriter().append('"'); + return; + } + context.getWriter().append("$rt_ustr("); + context.writeExpr(expr); + context.getWriter().append(")"); + } + + private boolean isValidIndentifier(String str) { + if (!Character.isJavaIdentifierStart(str.charAt(0))) { + return false; + } + for (int i = 1; i < str.length(); ++i) { + if (!Character.isJavaIdentifierPart(str.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java new file mode 100644 index 000000000..bb0ae0c26 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodHolder; +import org.teavm.vm.spi.TeaVMHost; + +/** + * + * @author Alexey Andreev + */ +class ResourceAccessorTransformer implements ClassHolderTransformer { + private TeaVMHost vm; + + public ResourceAccessorTransformer(TeaVMHost vm) { + this.vm = vm; + } + + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { + ResourceAccessorGenerator generator = new ResourceAccessorGenerator(); + if (cls.getName().equals(ResourceAccessor.class)) { + for (MethodHolder method : cls.getMethods()) { + vm.add(method.getReference(), generator); + } + } + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java new file mode 100644 index 000000000..cbb343123 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.plugin; + +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; + +/** + * + * @author Alexey Andreev + */ +class ResourceTransformer implements ClassHolderTransformer { + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { + } +} From 7e3d62a85d0717b2500056923595ef11b1abc70e Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Thu, 5 Jun 2014 17:22:02 +0400 Subject: [PATCH 06/16] Starts implementation of resource access transformer --- .../platform/plugin/ResourceTransformer.java | 117 +++++++++++++++++- 1 file changed, 114 insertions(+), 3 deletions(-) diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java index cbb343123..c767f169d 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -15,9 +15,15 @@ */ package org.teavm.platform.plugin; -import org.teavm.model.ClassHolder; -import org.teavm.model.ClassHolderTransformer; -import org.teavm.model.ClassReaderSource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.teavm.model.*; +import org.teavm.model.instructions.EmptyInstruction; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.StringConstantInstruction; +import org.teavm.platform.metadata.Resource; /** * @@ -26,5 +32,110 @@ import org.teavm.model.ClassReaderSource; class ResourceTransformer implements ClassHolderTransformer { @Override public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { + for (MethodHolder method : cls.getMethods()) { + Program program = method.getProgram(); + if (program != null) { + transformProgram(innerSource, program); + } + } + } + + private void transformProgram(ClassReaderSource innerSource, Program program) { + for (int i = 0; i < program.basicBlockCount(); ++i) { + transformBasicBlock(innerSource, program.basicBlockAt(i)); + } + } + + private void transformBasicBlock(ClassReaderSource innerSource, BasicBlock block) { + List instructions = block.getInstructions(); + for (int i = 0; i < instructions.size(); ++i) { + Instruction insn = instructions.get(i); + if (insn instanceof InvokeInstruction) { + InvokeInstruction invoke = (InvokeInstruction)insn; + List replacement = transformInvoke(innerSource, invoke); + if (replacement != null) { + instructions.set(i, new EmptyInstruction()); + instructions.addAll(i, replacement); + i += replacement.size(); + } + } + } + } + + private List transformInvoke(ClassReaderSource innerSource, InvokeInstruction insn) { + if (insn.getType() != InvocationType.VIRTUAL) { + return null; + } + MethodReference method = insn.getMethod(); + ClassReader iface = innerSource.get(method.getClassName()); + if (iface.getAnnotations().get(Resource.class.getName()) == null) { + return null; + } + if (method.getName().startsWith("get")) { + if (method.getName().length() > 3) { + return transformGetterInvocation(insn, method.getName().substring(3)); + } + } else if (method.getName().startsWith("is")) { + if (method.getName().length() > 2) { + return transformGetterInvocation(insn, method.getName().substring(2)); + } + } else if (method.getName().startsWith("set")) { + if (method.getName().length() > 3) { + return transformSetterInvocation(insn, method.getName().substring(3)); + } + } + return null; + } + + private List transformGetterInvocation(InvokeInstruction insn, String property) { + if (insn.getReceiver() == null) { + return Collections.emptyList(); + } + ValueType type = insn.getMethod().getDescriptor().getResultType(); + Program program = insn.getProgram(); + List instructions = new ArrayList<>(); + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case BOOLEAN: + Variable nameVar = program.createVariable(); + Variable resultVar = program.createVariable(); + StringConstantInstruction nameInsn = new StringConstantInstruction(); + nameInsn.setConstant(property); + nameInsn.setReceiver(nameVar); + instructions.add(nameInsn); + InvokeInstruction accessorInvoke = new InvokeInstruction(); + accessorInvoke.setType(InvocationType.SPECIAL); + accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get", + Object.class, String.class, Object.class)); + accessorInvoke.getArguments().add(insn.getInstance()); + accessorInvoke.getArguments().add(nameVar); + accessorInvoke.setReceiver(resultVar); + instructions.add(accessorInvoke); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castToBoolean", Object.class, + boolean.class)); + castInvoke.getArguments().add(resultVar); + castInvoke.setReceiver(insn.getReceiver()); + return instructions; + } + } else if (type instanceof ValueType.Object) { + + } + return null; + } + + private List transformSetterInvocation(InvokeInstruction insn, String property) { + return null; + } + + private String getPropertyName(String name) { + if (name.length() == 1) { + return name; + } + if (Character.isUpperCase(name.charAt(1))) { + return name; + } + return Character.toLowerCase(name.charAt(0)) + name.substring(1); } } From 8956e8b8d0546da58d77d3145cc9ebee62ce83d4 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 6 Jun 2014 17:02:34 +0400 Subject: [PATCH 07/16] Implements transformer that substitutes all invocations of resources with ResourcesAccessor --- .../platform/plugin/ResourceAccessor.java | 4 +- .../platform/plugin/ResourceTransformer.java | 151 ++++++++++++++---- 2 files changed, 124 insertions(+), 31 deletions(-) diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java index e91ca89c2..7c399174b 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java @@ -26,11 +26,11 @@ final class ResourceAccessor { public static native Object get(Object obj, int index); - public static native Object add(Object obj, Object elem); + public static native void add(Object obj, Object elem); public static native boolean has(Object obj, String key); - public static native boolean size(Object obj); + public static native int size(Object obj); public static native int castToInt(Object obj); diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java index c767f169d..ee4d82b4d 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -16,14 +16,14 @@ package org.teavm.platform.plugin; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.teavm.model.*; -import org.teavm.model.instructions.EmptyInstruction; -import org.teavm.model.instructions.InvocationType; -import org.teavm.model.instructions.InvokeInstruction; -import org.teavm.model.instructions.StringConstantInstruction; +import org.teavm.model.instructions.*; import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; /** * @@ -67,21 +67,35 @@ class ResourceTransformer implements ClassHolderTransformer { return null; } MethodReference method = insn.getMethod(); + if (method.getClassName().equals(ResourceArray.class.getName()) || + method.getClassName().equals(ResourceMap.class.getName())) { + InvokeInstruction accessInsn = new InvokeInstruction(); + accessInsn.setType(InvocationType.SPECIAL); + ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2]; + types[0] = ValueType.object("java.lang.Object"); + System.arraycopy(method.getDescriptor().getSignature(), 0, types, 1, + method.getDescriptor().parameterCount() + 1); + accessInsn.setMethod(new MethodReference(ResourceAccessor.class.getName(), method.getName(), types)); + accessInsn.getArguments().add(insn.getInstance()); + accessInsn.getArguments().addAll(insn.getArguments()); + accessInsn.setReceiver(insn.getReceiver()); + return Arrays.asList(accessInsn); + } ClassReader iface = innerSource.get(method.getClassName()); if (iface.getAnnotations().get(Resource.class.getName()) == null) { return null; } if (method.getName().startsWith("get")) { if (method.getName().length() > 3) { - return transformGetterInvocation(insn, method.getName().substring(3)); + return transformGetterInvocation(insn, getPropertyName(method.getName().substring(3))); } } else if (method.getName().startsWith("is")) { if (method.getName().length() > 2) { - return transformGetterInvocation(insn, method.getName().substring(2)); + return transformGetterInvocation(insn, getPropertyName(method.getName().substring(2))); } } else if (method.getName().startsWith("set")) { if (method.getName().length() > 3) { - return transformSetterInvocation(insn, method.getName().substring(3)); + return transformSetterInvocation(insn, getPropertyName(method.getName().substring(3))); } } return null; @@ -92,39 +106,118 @@ class ResourceTransformer implements ClassHolderTransformer { return Collections.emptyList(); } ValueType type = insn.getMethod().getDescriptor().getResultType(); - Program program = insn.getProgram(); List instructions = new ArrayList<>(); if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive)type).getKind()) { case BOOLEAN: - Variable nameVar = program.createVariable(); - Variable resultVar = program.createVariable(); - StringConstantInstruction nameInsn = new StringConstantInstruction(); - nameInsn.setConstant(property); - nameInsn.setReceiver(nameVar); - instructions.add(nameInsn); - InvokeInstruction accessorInvoke = new InvokeInstruction(); - accessorInvoke.setType(InvocationType.SPECIAL); - accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get", - Object.class, String.class, Object.class)); - accessorInvoke.getArguments().add(insn.getInstance()); - accessorInvoke.getArguments().add(nameVar); - accessorInvoke.setReceiver(resultVar); - instructions.add(accessorInvoke); - InvokeInstruction castInvoke = new InvokeInstruction(); - castInvoke.setType(InvocationType.SPECIAL); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castToBoolean", Object.class, - boolean.class)); - castInvoke.getArguments().add(resultVar); - castInvoke.setReceiver(insn.getReceiver()); + getAndCastProperty(insn, property, instructions, boolean.class); return instructions; + case BYTE: + getAndCastProperty(insn, property, instructions, byte.class); + return instructions; + case SHORT: + getAndCastProperty(insn, property, instructions, short.class); + return instructions; + case INTEGER: + getAndCastProperty(insn, property, instructions, int.class); + return instructions; + case FLOAT: + getAndCastProperty(insn, property, instructions, float.class); + return instructions; + case DOUBLE: + getAndCastProperty(insn, property, instructions, double.class); + return instructions; + case CHARACTER: + case LONG: + break; } } else if (type instanceof ValueType.Object) { - + switch (((ValueType.Object)type).getClassName()) { + case "java.lang.Boolean": + getAndCastPropertyToWrapper(insn, property, instructions, boolean.class, Boolean.class); + return instructions; + case "java.lang.Byte": + getAndCastPropertyToWrapper(insn, property, instructions, byte.class, Byte.class); + return instructions; + case "java.lang.Short": + getAndCastPropertyToWrapper(insn, property, instructions, short.class, Short.class); + return instructions; + case "java.lang.Integer": + getAndCastPropertyToWrapper(insn, property, instructions, int.class, Integer.class); + return instructions; + case "java.lang.Float": + getAndCastPropertyToWrapper(insn, property, instructions, float.class, Float.class); + return instructions; + case "java.lang.Double": + getAndCastPropertyToWrapper(insn, property, instructions, double.class, Double.class); + return instructions; + default: { + Variable resultVar = insn.getProgram().createVariable(); + getProperty(insn, property, instructions, resultVar); + CastInstruction castInsn = new CastInstruction(); + castInsn.setReceiver(insn.getReceiver()); + castInsn.setTargetType(type); + castInsn.setValue(resultVar); + instructions.add(castInsn); + return instructions; + } + } } return null; } + private void getProperty(InvokeInstruction insn, String property, List instructions, + Variable resultVar) { + Program program = insn.getProgram(); + Variable nameVar = program.createVariable(); + StringConstantInstruction nameInsn = new StringConstantInstruction(); + nameInsn.setConstant(property); + nameInsn.setReceiver(nameVar); + instructions.add(nameInsn); + InvokeInstruction accessorInvoke = new InvokeInstruction(); + accessorInvoke.setType(InvocationType.SPECIAL); + accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get", + Object.class, String.class, Object.class)); + accessorInvoke.getArguments().add(insn.getInstance()); + accessorInvoke.getArguments().add(nameVar); + accessorInvoke.setReceiver(resultVar); + instructions.add(accessorInvoke); + } + + private void getAndCastProperty(InvokeInstruction insn, String property, List instructions, + Class primitive) { + Program program = insn.getProgram(); + Variable resultVar = program.createVariable(); + getProperty(insn, property, instructions, resultVar); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + String primitiveCapitalized = primitive.getName(); + primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + + primitiveCapitalized.substring(1); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized, + Object.class, primitive)); + castInvoke.getArguments().add(resultVar); + castInvoke.setReceiver(insn.getReceiver()); + instructions.add(castInvoke); + } + + private void getAndCastPropertyToWrapper(InvokeInstruction insn, String property, List instructions, + Class primitive, Class wrapper) { + Program program = insn.getProgram(); + Variable resultVar = program.createVariable(); + getProperty(insn, property, instructions, resultVar); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + String primitiveCapitalized = primitive.getName(); + primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + + primitiveCapitalized.substring(1); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Primitive", + Object.class, wrapper)); + castInvoke.getArguments().add(resultVar); + castInvoke.setReceiver(insn.getReceiver()); + instructions.add(castInvoke); + } + private List transformSetterInvocation(InvokeInstruction insn, String property) { return null; } From c6e7b30bedb40dff369ebfeba44d4cc234aacf18 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 6 Jun 2014 18:18:52 +0400 Subject: [PATCH 08/16] Adds tests for metadata generators --- pom.xml | 5 + teavm-classlib/pom.xml | 22 +++++ .../metadata/DependentTestResource.java | 5 +- .../metadata/MetadataGeneratorTest.java | 59 +++++++++++ .../teavm/platform/metadata/TestResource.java | 99 +++++++++++++++++++ .../metadata/TestResourceGenerator.java | 65 ++++++++++++ .../java/org/teavm/javascript/Decompiler.java | 1 + .../MetadataProviderNativeGenerator.java | 2 +- .../plugin/MetadataProviderTransformer.java | 5 +- .../plugin/ResourceAccessorTransformer.java | 4 +- .../platform/plugin/ResourceTransformer.java | 2 +- .../teavm/platform/metadata/TestResource.java | 31 ------ 12 files changed, 262 insertions(+), 38 deletions(-) rename teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java => teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java (87%) create mode 100644 teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java create mode 100644 teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java delete mode 100644 teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java diff --git a/pom.xml b/pom.xml index c144930dc..6e0e1f8f5 100644 --- a/pom.xml +++ b/pom.xml @@ -178,6 +178,11 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 2.17 + diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml index 199365cb0..57d85caa8 100644 --- a/teavm-classlib/pom.xml +++ b/teavm-classlib/pom.xml @@ -30,6 +30,12 @@ junit test + + org.teavm + teavm-platform + ${project.version} + test + org.teavm teavm-core @@ -51,6 +57,13 @@ org.teavm teavm-maven-plugin ${project.version} + + + org.teavm + teavm-platform + ${project.version} + + generate-javascript-tests @@ -96,6 +109,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org/teavm/platform/metadata/*.java + + + org.apache.maven.plugins maven-source-plugin diff --git a/teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java similarity index 87% rename from teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java rename to teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java index 4f240dd36..3415d46a1 100644 --- a/teavm-platform/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java @@ -19,6 +19,9 @@ package org.teavm.platform.metadata; * * @author Alexey Andreev */ -public class MetadataGeneratorTest { +@Resource +public interface DependentTestResource { + String getBar(); + void setBar(String bar); } diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java new file mode 100644 index 000000000..280ee32f5 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class MetadataGeneratorTest { + @MetadataProvider(TestResourceGenerator.class) + private native TestResource getNull(); + + @Test + public void nullExposed() { + assertNull(getNull()); + } + + @MetadataProvider(TestResourceGenerator.class) + private native TestResource getInt(); + + @Test + public void intExposed() { + assertEquals(23, getInt()); + } + + @MetadataProvider(TestResourceGenerator.class) + private native TestResource getResource(); + + @Test + public void resourceObjectExposed() { + TestResource res = getResource(); + assertEquals(23, res.getA()); + assertFalse(res.getB()); + assertEquals(24, res.getD()); + assertEquals(25, res.getE()); + assertEquals(3.14, res.getF(), 0.001); + assertEquals(2.72, res.getG(), 0.001); + assertEquals(Integer.valueOf(26), res.getH()); + assertNull(res.getI()); + assertEquals(Byte.valueOf((byte)27), res.getJ()); + assertEquals(Short.valueOf((short)28), res.getK()); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java new file mode 100644 index 000000000..d64800bbd --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java @@ -0,0 +1,99 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +@Resource +public interface TestResource { + int getA(); + + void setA(int a); + + boolean getB(); + + void setB(boolean b); + + byte getD(); + + void setD(byte d); + + short getE(); + + void setE(short e); + + float getF(); + + void setF(float f); + + double getG(); + + void setG(double g); + + Integer getH(); + + void setH(Integer a); + + Boolean getI(); + + void setI(Boolean i); + + Byte getJ(); + + void setJ(Byte d); + + Short getK(); + + void setK(Short k); + + Float getL(); + + void setL(Float l); + + Double getM(); + + void setM(Double g); + + String getFoo(); + + void setFoo(String foo); + + ResourceArray getArrayA(); + + void setArrayA(ResourceArray arrayA); + + ResourceArray getArrayB(); + + void setArrayB(ResourceArray arrayB); + + ResourceArray> getArrayC(); + + void setArrayC(ResourceArray> arrayC); + + ResourceMap getMapA(); + + void setMapA(ResourceMap mapA); + + ResourceMap getMapB(); + + void setMapB(ResourceMap mapB); + + ResourceMap> getMapC(); + + void setMapC(ResourceMap> mapC); +} diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java new file mode 100644 index 000000000..6b787b37c --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class TestResourceGenerator implements MetadataGenerator { + @Override + public Object generateMetadata(MetadataGeneratorContext context, MethodReference method) { + switch (method.getName()) { + case "getNull": + return null; + case "getInt": + return 23; + case "getResource": + return getResource(context); + default: + throw new RuntimeException("Unsupported method: " + method); + } + } + + private Object getResource(MetadataGeneratorContext context) { + TestResource resource = context.createResource(TestResource.class); + resource.setA(23); + resource.setB(false); + resource.setD((byte)24); + resource.setE((short)25); + resource.setF(3.14f); + resource.setG(2.72); + resource.setH(26); + resource.setI(null); + resource.setJ((byte)27); + resource.setK((short)28); + resource.setL(100f); + resource.setM(200.0); + + ResourceArray array = context.createResourceArray(); + array.add(2); + array.add(3); + resource.setArrayA(array); + DependentTestResource dep = context.createResource(DependentTestResource.class); + dep.setBar("baz"); + ResourceArray resArray = context.createResourceArray(); + resArray.add(dep); + resource.setArrayB(resArray); + return resource; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index 72f45cefc..35d7f5fdd 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -85,6 +85,7 @@ public class Decompiler { @Override public void run() { Decompiler copy = new Decompiler(classSource, classLoader, executor); copy.generators = generators; + copy.methodsToPass = methodsToPass; result.set(index, copy.decompile(classSource.get(className))); } }); 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 2d43695e4..62497207d 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 @@ -74,7 +74,7 @@ public class MetadataProviderNativeGenerator implements Generator { // Generate resource loader Object resource = generator.generateMetadata(metadataContext, methodRef); - writer.append("if (!").appendMethodBody(methodRef).append("$$resource === undefined").append(") {") + writer.append("if (!window.hasOwnProperty(\"").appendMethodBody(methodRef).append("$$resource\")) {") .indent().softNewLine(); writer.appendMethodBody(methodRef).append("$$resource = "); ResourceWriterHelper.write(writer, resource); 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 703052af3..f8d25aadf 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 @@ -29,10 +29,11 @@ class MetadataProviderTransformer implements ClassHolderTransformer { for (MethodHolder method : cls.getMethods()) { AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); if (providerAnnot == null) { - return; + continue; } AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName()); - genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(null))); + genAnnot.getValues().put("value", new AnnotationValue(ValueType.object( + MetadataProviderNativeGenerator.class.getName()))); method.getAnnotations().add(genAnnot); } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java index bb0ae0c26..6a3819013 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java @@ -34,8 +34,8 @@ class ResourceAccessorTransformer implements ClassHolderTransformer { @Override public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { - ResourceAccessorGenerator generator = new ResourceAccessorGenerator(); - if (cls.getName().equals(ResourceAccessor.class)) { + if (cls.getName().equals(ResourceAccessor.class.getName())) { + ResourceAccessorGenerator generator = new ResourceAccessorGenerator(); for (MethodHolder method : cls.getMethods()) { vm.add(method.getReference(), generator); } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java index ee4d82b4d..cf7f0b4af 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -211,7 +211,7 @@ class ResourceTransformer implements ClassHolderTransformer { String primitiveCapitalized = primitive.getName(); primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + primitiveCapitalized.substring(1); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Primitive", + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Wrapper", Object.class, wrapper)); castInvoke.getArguments().add(resultVar); castInvoke.setReceiver(insn.getReceiver()); diff --git a/teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java b/teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java deleted file mode 100644 index d75474deb..000000000 --- a/teavm-platform/src/test/java/org/teavm/platform/metadata/TestResource.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.platform.metadata; - -/** - * - * @author Alexey Andreev - */ -@Resource -public interface TestResource { - int getInt(); - - void setInt(int value); - - String getString(); - - void setString(String string); -} From 6992c81e8de07811968a94a0f10d88598c141be1 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 7 Jun 2014 15:48:57 +0400 Subject: [PATCH 09/16] Makes all current metadata provider tests passing --- .../metadata/MetadataGeneratorTest.java | 2 +- .../java/org/teavm/javascript/Decompiler.java | 2 +- .../plugin/BuildTimeResourceProxyBuilder.java | 16 +++++- .../plugin/ResourceAccessorGenerator.java | 56 +++++++++++-------- .../platform/plugin/ResourceTransformer.java | 2 +- .../teavm/platform/plugin/ResourceWriter.java | 2 +- 6 files changed, 50 insertions(+), 30 deletions(-) diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java index 280ee32f5..49de83443 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -32,7 +32,7 @@ public class MetadataGeneratorTest { } @MetadataProvider(TestResourceGenerator.class) - private native TestResource getInt(); + private native int getInt(); @Test public void intExposed() { diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index 35d7f5fdd..9a0e8078e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -132,7 +132,7 @@ public class Decompiler { continue; } if (method.getAnnotations().get(InjectedBy.class.getName()) != null || - methodsToPass.contains(method)) { + methodsToPass.contains(method.getReference())) { continue; } MethodNode methodNode = decompile(method); 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 0baf44b47..f288a4cfd 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 @@ -17,6 +17,7 @@ package org.teavm.platform.plugin; import java.lang.reflect.Method; import java.util.*; +import org.teavm.codegen.SourceWriter; import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.ResourceArray; import org.teavm.platform.metadata.ResourceMap; @@ -73,11 +74,22 @@ class BuildTimeResourceProxyBuilder { " that is not an interface"); } scanIface(rootIface); + Method writeMethod; + try { + writeMethod = ResourceWriter.class.getMethod("write", SourceWriter.class); + } catch (NoSuchMethodException e) { + throw new AssertionError("Method must exist", e); + } + String[] properties = new String[propertyIndexes.size()]; + for (Map.Entry entry : propertyIndexes.entrySet()) { + properties[entry.getValue()] = entry.getKey(); + } + methods.put(writeMethod, new BuildTimeResourceWriterMethod(properties)); return new BuildTimeResourceProxyFactory(methods, initialData); } private void scanIface(Class iface) { - if (iface.isAnnotationPresent(Resource.class)) { + if (!iface.isAnnotationPresent(Resource.class)) { throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + ". This type is not marked with the " + Resource.class.getName() + " annotation"); } @@ -170,7 +182,7 @@ class BuildTimeResourceProxyBuilder { } private void scanSetter(Method method) { - String propertyName = extractPropertyName(method.getName().substring(2)); + String propertyName = extractPropertyName(method.getName().substring(3)); if (propertyName == null || !method.getReturnType().equals(void.class) || method.getParameterTypes().length != 1) { throwInvalidMethod(method); 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 5e8d7542d..746e3e9b7 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 @@ -33,43 +33,43 @@ class ResourceAccessorGenerator implements Injector { switch (methodRef.getName()) { case "get": if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) { - context.writeExpr(context.getArgument(1)); + context.writeExpr(context.getArgument(0)); context.getWriter().append('['); - context.writeExpr(context.getArgument(2)); + context.writeExpr(context.getArgument(1)); context.getWriter().append(']'); } else { - context.writeExpr(context.getArgument(1)); - writePropertyAccessor(context, context.getArgument(2)); + context.writeExpr(context.getArgument(0)); + writePropertyAccessor(context, context.getArgument(1)); } break; case "put": context.getWriter().append('('); if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) { - context.writeExpr(context.getArgument(1)); + context.writeExpr(context.getArgument(0)); context.getWriter().append('['); - context.writeExpr(context.getArgument(2)); - } else { context.writeExpr(context.getArgument(1)); - writePropertyAccessor(context, context.getArgument(2)); + } else { + context.writeExpr(context.getArgument(0)); + writePropertyAccessor(context, context.getArgument(1)); } context.getWriter().append(']').ws().append('=').ws(); - context.writeExpr(context.getArgument(3)); - context.getWriter().append(')'); - break; - case "add": - context.writeExpr(context.getArgument(1)); - context.getWriter().append(".push("); context.writeExpr(context.getArgument(2)); context.getWriter().append(')'); break; - case "has": + case "add": + context.writeExpr(context.getArgument(0)); + context.getWriter().append(".push("); context.writeExpr(context.getArgument(1)); + context.getWriter().append(')'); + break; + case "has": + context.writeExpr(context.getArgument(0)); context.getWriter().append(".hasOwnProperty("); - writeStringExpr(context, context.getArgument(2)); + writeStringExpr(context, context.getArgument(1)); context.getWriter().append(')'); break; case "size": - context.writeExpr(context.getArgument(1)); + context.writeExpr(context.getArgument(0)); context.getWriter().append(".length"); break; case "castToInt": @@ -84,7 +84,7 @@ class ResourceAccessorGenerator implements Injector { case "castFromBoolean": case "castFromFloat": case "castFromDouble": - context.writeExpr(context.getArgument(1)); + context.writeExpr(context.getArgument(0)); break; case "castToIntWrapper": castToWrapper(context, Integer.class, int.class); @@ -124,29 +124,37 @@ class ResourceAccessorGenerator implements Injector { break; case "castToString": context.getWriter().append("$rt_str("); - context.writeExpr(context.getArgument(1)); + context.writeExpr(context.getArgument(0)); context.getWriter().append(")"); break; case "castFromString": context.getWriter().append("$rt_ustr("); - context.writeExpr(context.getArgument(1)); + context.writeExpr(context.getArgument(0)); context.getWriter().append(")"); break; } } private void castToWrapper(InjectorContext context, Class wrapper, Class primitive) throws IOException { + context.getWriter().append('('); + context.writeExpr(context.getArgument(0)); + context.getWriter().ws().append("==").ws().append("null").ws().append('?').ws().append("null") + .ws().append(':').ws(); context.getWriter().appendMethodBody(new MethodReference(wrapper, "valueOf", primitive, wrapper)).append('('); - context.writeExpr(context.getArgument(1)); - context.getWriter().append(')'); + context.writeExpr(context.getArgument(0)); + context.getWriter().append("))"); } private void castFromWrapper(InjectorContext context, Class wrapper, Class primitive) throws IOException { + context.getWriter().append('('); + context.writeExpr(context.getArgument(0)); + context.getWriter().ws().append("==").ws().append("null").ws().append('?').ws().append("null") + .ws().append(':').ws(); String primitiveName = primitive.getName(); context.getWriter().appendMethodBody(new MethodReference(wrapper, primitiveName + "Value", primitive)) .append('('); - context.writeExpr(context.getArgument(1)); - context.getWriter().append(')'); + context.writeExpr(context.getArgument(0)); + context.getWriter().append("))"); } private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException { diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java index cf7f0b4af..9e33c292f 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -224,7 +224,7 @@ class ResourceTransformer implements ClassHolderTransformer { private String getPropertyName(String name) { if (name.length() == 1) { - return name; + return name.toLowerCase(); } if (Character.isUpperCase(name.charAt(1))) { return name; diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java index 5f1be5689..b457a5d70 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java @@ -22,6 +22,6 @@ import org.teavm.codegen.SourceWriter; * * @author Alexey Andreev */ -interface ResourceWriter { +public interface ResourceWriter { void write(SourceWriter writer) throws IOException; } From a5f9e4a0b4b4efac62995b38ac28c48ccf11c8d0 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 8 Jun 2014 11:38:51 +0400 Subject: [PATCH 10/16] Bugfixes --- .../metadata/MetadataGeneratorTest.java | 13 + .../metadata/TestResourceGenerator.java | 1 + .../main/java/org/teavm/model/ValueType.java | 21 +- .../plugin/ResourceProgramTransformer.java | 295 ++++++++++++++++++ .../platform/plugin/ResourceTransformer.java | 202 +----------- 5 files changed, 330 insertions(+), 202 deletions(-) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java index 49de83443..1c56755aa 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -51,9 +51,22 @@ public class MetadataGeneratorTest { assertEquals(25, res.getE()); assertEquals(3.14, res.getF(), 0.001); assertEquals(2.72, res.getG(), 0.001); + assertEquals(Integer.valueOf(26), res.getH()); assertNull(res.getI()); assertEquals(Byte.valueOf((byte)27), res.getJ()); assertEquals(Short.valueOf((short)28), res.getK()); + assertEquals(100, res.getL().floatValue(), 0.1); + assertEquals(200, res.getM().doubleValue(), 0.1); + + assertEquals("qwe", res.getFoo()); + + assertEquals(2, res.getArrayA().size()); + assertEquals(Integer.valueOf(2), res.getArrayA().get(0)); + assertEquals(Integer.valueOf(3), res.getArrayA().get(1)); + assertEquals(1, res.getArrayB().size()); + assertEquals("baz", res.getArrayB().get(0)); + assertNull(res.getArrayC()); } } + diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java index 6b787b37c..2dbf1e392 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java @@ -50,6 +50,7 @@ public class TestResourceGenerator implements MetadataGenerator { resource.setK((short)28); resource.setL(100f); resource.setM(200.0); + resource.setFoo("qwe"); ResourceArray array = context.createResourceArray(); array.add(2); diff --git a/teavm-core/src/main/java/org/teavm/model/ValueType.java b/teavm-core/src/main/java/org/teavm/model/ValueType.java index b4f7795ab..53547517b 100644 --- a/teavm-core/src/main/java/org/teavm/model/ValueType.java +++ b/teavm-core/src/main/java/org/teavm/model/ValueType.java @@ -191,7 +191,26 @@ public abstract class ValueType { } public static ValueType primitive(PrimitiveType type) { - return new Primitive(type); + switch (type) { + case BOOLEAN: + return BOOLEAN; + case BYTE: + return BYTE; + case CHARACTER: + return CHARACTER; + case SHORT: + return SHORT; + case INTEGER: + return INTEGER; + case LONG: + return LONG; + case FLOAT: + return FLOAT; + case DOUBLE: + return DOUBLE; + default: + throw new AssertionError("Unknown primitive type " + type); + } } public static ValueType[] parseMany(String text) { diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java new file mode 100644 index 000000000..4b542a767 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -0,0 +1,295 @@ +package org.teavm.platform.plugin; + +import java.util.*; +import org.teavm.model.*; +import org.teavm.model.instructions.*; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; + +/** + * + * @author Alexey Andreev + */ +class ResourceProgramTransformer { + private ClassReaderSource innerSource; + private Program program; + private Set arrayItemsVars = new HashSet<>(); + + public ResourceProgramTransformer(ClassReaderSource innerSource, Program program) { + this.innerSource = innerSource; + this.program = program; + } + + public void transformProgram() { + for (int i = 0; i < program.basicBlockCount(); ++i) { + transformBasicBlock(program.basicBlockAt(i)); + } + if (!arrayItemsVars.isEmpty()) { + for (int i = 0; i < program.basicBlockCount(); ++i) { + postProcessBasicBlock(program.basicBlockAt(i)); + } + } + } + + private void transformBasicBlock(BasicBlock block) { + List instructions = block.getInstructions(); + for (int i = 0; i < instructions.size(); ++i) { + Instruction insn = instructions.get(i); + if (insn instanceof InvokeInstruction) { + InvokeInstruction invoke = (InvokeInstruction)insn; + List replacement = transformInvoke(invoke); + if (replacement != null) { + instructions.set(i, new EmptyInstruction()); + instructions.addAll(i, replacement); + i += replacement.size(); + } + } + } + } + + private void postProcessBasicBlock(BasicBlock block) { + List instructions = block.getInstructions(); + for (int i = 0; i < instructions.size(); ++i) { + Instruction insn = instructions.get(i); + if (!(insn instanceof CastInstruction)) { + continue; + } + CastInstruction cast = (CastInstruction)insn; + if (!arrayItemsVars.contains(cast.getReceiver())) { + continue; + } + if (!(cast.getTargetType() instanceof ValueType.Object)) { + continue; + } + String targetTypeName = ((ValueType.Object)cast.getTargetType()).getClassName(); + Variable var = cast.getValue(); + Variable recv = cast.getReceiver(); + switch (targetTypeName) { + case "java.lang.Integer": + instructions.set(i, castToWrapper(var, recv, int.class, Integer.class)); + break; + case "java.lang.Boolean": + instructions.set(i, castToWrapper(var, recv, boolean.class, Boolean.class)); + break; + case "java.lang.Byte": + instructions.set(i, castToWrapper(var, recv, byte.class, Byte.class)); + break; + case "java.lang.Short": + instructions.set(i, castToWrapper(var, recv, short.class, Short.class)); + break; + case "java.lang.Float": + instructions.set(i, castToWrapper(var, recv, float.class, Float.class)); + break; + case "java.lang.Double": + instructions.set(i, castToWrapper(var, recv, double.class, Double.class)); + break; + case "java.lang.String": { + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castToString", + Object.class, String.class)); + castInvoke.getArguments().add(var); + castInvoke.setReceiver(recv); + instructions.set(i, castInvoke); + break; + } + } + } + } + + private List transformInvoke(InvokeInstruction insn) { + if (insn.getType() != InvocationType.VIRTUAL) { + return null; + } + MethodReference method = insn.getMethod(); + if (method.getClassName().equals(ResourceArray.class.getName()) || + method.getClassName().equals(ResourceMap.class.getName())) { + if (method.getName().equals("get")) { + arrayItemsVars.add(insn.getReceiver()); + } + InvokeInstruction accessInsn = new InvokeInstruction(); + accessInsn.setType(InvocationType.SPECIAL); + ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2]; + types[0] = ValueType.object("java.lang.Object"); + System.arraycopy(method.getDescriptor().getSignature(), 0, types, 1, + method.getDescriptor().parameterCount() + 1); + accessInsn.setMethod(new MethodReference(ResourceAccessor.class.getName(), method.getName(), types)); + accessInsn.getArguments().add(insn.getInstance()); + accessInsn.getArguments().addAll(insn.getArguments()); + accessInsn.setReceiver(insn.getReceiver()); + return Arrays.asList(accessInsn); + } + ClassReader iface = innerSource.get(method.getClassName()); + if (iface.getAnnotations().get(Resource.class.getName()) == null) { + return null; + } + if (method.getName().startsWith("get")) { + if (method.getName().length() > 3) { + return transformGetterInvocation(insn, getPropertyName(method.getName().substring(3))); + } + } else if (method.getName().startsWith("is")) { + if (method.getName().length() > 2) { + return transformGetterInvocation(insn, getPropertyName(method.getName().substring(2))); + } + } else if (method.getName().startsWith("set")) { + if (method.getName().length() > 3) { + return transformSetterInvocation(insn, getPropertyName(method.getName().substring(3))); + } + } + return null; + } + + private List transformGetterInvocation(InvokeInstruction insn, String property) { + if (insn.getReceiver() == null) { + return Collections.emptyList(); + } + ValueType type = insn.getMethod().getDescriptor().getResultType(); + List instructions = new ArrayList<>(); + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case BOOLEAN: + getAndCastProperty(insn, property, instructions, boolean.class); + return instructions; + case BYTE: + getAndCastProperty(insn, property, instructions, byte.class); + return instructions; + case SHORT: + getAndCastProperty(insn, property, instructions, short.class); + return instructions; + case INTEGER: + getAndCastProperty(insn, property, instructions, int.class); + return instructions; + case FLOAT: + getAndCastProperty(insn, property, instructions, float.class); + return instructions; + case DOUBLE: + getAndCastProperty(insn, property, instructions, double.class); + return instructions; + case CHARACTER: + case LONG: + break; + } + } else if (type instanceof ValueType.Object) { + switch (((ValueType.Object)type).getClassName()) { + case "java.lang.Boolean": + getAndCastPropertyToWrapper(insn, property, instructions, boolean.class, Boolean.class); + return instructions; + case "java.lang.Byte": + getAndCastPropertyToWrapper(insn, property, instructions, byte.class, Byte.class); + return instructions; + case "java.lang.Short": + getAndCastPropertyToWrapper(insn, property, instructions, short.class, Short.class); + return instructions; + case "java.lang.Integer": + getAndCastPropertyToWrapper(insn, property, instructions, int.class, Integer.class); + return instructions; + case "java.lang.Float": + getAndCastPropertyToWrapper(insn, property, instructions, float.class, Float.class); + return instructions; + case "java.lang.Double": + getAndCastPropertyToWrapper(insn, property, instructions, double.class, Double.class); + return instructions; + case "java.lang.String": { + Variable resultVar = insn.getProgram().createVariable(); + getProperty(insn, property, instructions, resultVar); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castToString", + Object.class, String.class)); + castInvoke.getArguments().add(resultVar); + castInvoke.setReceiver(insn.getReceiver()); + instructions.add(castInvoke); + return instructions; + } + default: { + Variable resultVar = insn.getProgram().createVariable(); + getProperty(insn, property, instructions, resultVar); + CastInstruction castInsn = new CastInstruction(); + castInsn.setReceiver(insn.getReceiver()); + castInsn.setTargetType(type); + castInsn.setValue(resultVar); + instructions.add(castInsn); + return instructions; + } + } + } + return null; + } + + private void getProperty(InvokeInstruction insn, String property, List instructions, + Variable resultVar) { + Variable nameVar = program.createVariable(); + StringConstantInstruction nameInsn = new StringConstantInstruction(); + nameInsn.setConstant(property); + nameInsn.setReceiver(nameVar); + instructions.add(nameInsn); + InvokeInstruction accessorInvoke = new InvokeInstruction(); + accessorInvoke.setType(InvocationType.SPECIAL); + accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get", + Object.class, String.class, Object.class)); + accessorInvoke.getArguments().add(insn.getInstance()); + accessorInvoke.getArguments().add(nameVar); + accessorInvoke.setReceiver(resultVar); + instructions.add(accessorInvoke); + } + + private void getAndCastProperty(InvokeInstruction insn, String property, List instructions, + Class primitive) { + Variable resultVar = program.createVariable(); + getProperty(insn, property, instructions, resultVar); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + String primitiveCapitalized = primitive.getName(); + primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + + primitiveCapitalized.substring(1); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized, + Object.class, primitive)); + castInvoke.getArguments().add(resultVar); + castInvoke.setReceiver(insn.getReceiver()); + instructions.add(castInvoke); + } + + private Instruction castToWrapper(Variable var, Variable receiver, Class primitive, Class wrapper) { + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + String primitiveCapitalized = primitive.getName(); + primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + + primitiveCapitalized.substring(1); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Wrapper", + Object.class, wrapper)); + castInvoke.getArguments().add(var); + castInvoke.setReceiver(receiver); + return castInvoke; + } + + private void getAndCastPropertyToWrapper(InvokeInstruction insn, String property, List instructions, + Class primitive, Class wrapper) { + Variable resultVar = program.createVariable(); + getProperty(insn, property, instructions, resultVar); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + String primitiveCapitalized = primitive.getName(); + primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + + primitiveCapitalized.substring(1); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Wrapper", + Object.class, wrapper)); + castInvoke.getArguments().add(resultVar); + castInvoke.setReceiver(insn.getReceiver()); + instructions.add(castInvoke); + } + + private List transformSetterInvocation(InvokeInstruction insn, String property) { + return null; + } + + private String getPropertyName(String name) { + if (name.length() == 1) { + return name.toLowerCase(); + } + if (Character.isUpperCase(name.charAt(1))) { + return name; + } + return Character.toLowerCase(name.charAt(0)) + name.substring(1); + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java index 9e33c292f..f0b8854d2 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -15,15 +15,7 @@ */ package org.teavm.platform.plugin; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import org.teavm.model.*; -import org.teavm.model.instructions.*; -import org.teavm.platform.metadata.Resource; -import org.teavm.platform.metadata.ResourceArray; -import org.teavm.platform.metadata.ResourceMap; /** * @@ -35,200 +27,8 @@ class ResourceTransformer implements ClassHolderTransformer { for (MethodHolder method : cls.getMethods()) { Program program = method.getProgram(); if (program != null) { - transformProgram(innerSource, program); + new ResourceProgramTransformer(innerSource, program).transformProgram(); } } } - - private void transformProgram(ClassReaderSource innerSource, Program program) { - for (int i = 0; i < program.basicBlockCount(); ++i) { - transformBasicBlock(innerSource, program.basicBlockAt(i)); - } - } - - private void transformBasicBlock(ClassReaderSource innerSource, BasicBlock block) { - List instructions = block.getInstructions(); - for (int i = 0; i < instructions.size(); ++i) { - Instruction insn = instructions.get(i); - if (insn instanceof InvokeInstruction) { - InvokeInstruction invoke = (InvokeInstruction)insn; - List replacement = transformInvoke(innerSource, invoke); - if (replacement != null) { - instructions.set(i, new EmptyInstruction()); - instructions.addAll(i, replacement); - i += replacement.size(); - } - } - } - } - - private List transformInvoke(ClassReaderSource innerSource, InvokeInstruction insn) { - if (insn.getType() != InvocationType.VIRTUAL) { - return null; - } - MethodReference method = insn.getMethod(); - if (method.getClassName().equals(ResourceArray.class.getName()) || - method.getClassName().equals(ResourceMap.class.getName())) { - InvokeInstruction accessInsn = new InvokeInstruction(); - accessInsn.setType(InvocationType.SPECIAL); - ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2]; - types[0] = ValueType.object("java.lang.Object"); - System.arraycopy(method.getDescriptor().getSignature(), 0, types, 1, - method.getDescriptor().parameterCount() + 1); - accessInsn.setMethod(new MethodReference(ResourceAccessor.class.getName(), method.getName(), types)); - accessInsn.getArguments().add(insn.getInstance()); - accessInsn.getArguments().addAll(insn.getArguments()); - accessInsn.setReceiver(insn.getReceiver()); - return Arrays.asList(accessInsn); - } - ClassReader iface = innerSource.get(method.getClassName()); - if (iface.getAnnotations().get(Resource.class.getName()) == null) { - return null; - } - if (method.getName().startsWith("get")) { - if (method.getName().length() > 3) { - return transformGetterInvocation(insn, getPropertyName(method.getName().substring(3))); - } - } else if (method.getName().startsWith("is")) { - if (method.getName().length() > 2) { - return transformGetterInvocation(insn, getPropertyName(method.getName().substring(2))); - } - } else if (method.getName().startsWith("set")) { - if (method.getName().length() > 3) { - return transformSetterInvocation(insn, getPropertyName(method.getName().substring(3))); - } - } - return null; - } - - private List transformGetterInvocation(InvokeInstruction insn, String property) { - if (insn.getReceiver() == null) { - return Collections.emptyList(); - } - ValueType type = insn.getMethod().getDescriptor().getResultType(); - List instructions = new ArrayList<>(); - if (type instanceof ValueType.Primitive) { - switch (((ValueType.Primitive)type).getKind()) { - case BOOLEAN: - getAndCastProperty(insn, property, instructions, boolean.class); - return instructions; - case BYTE: - getAndCastProperty(insn, property, instructions, byte.class); - return instructions; - case SHORT: - getAndCastProperty(insn, property, instructions, short.class); - return instructions; - case INTEGER: - getAndCastProperty(insn, property, instructions, int.class); - return instructions; - case FLOAT: - getAndCastProperty(insn, property, instructions, float.class); - return instructions; - case DOUBLE: - getAndCastProperty(insn, property, instructions, double.class); - return instructions; - case CHARACTER: - case LONG: - break; - } - } else if (type instanceof ValueType.Object) { - switch (((ValueType.Object)type).getClassName()) { - case "java.lang.Boolean": - getAndCastPropertyToWrapper(insn, property, instructions, boolean.class, Boolean.class); - return instructions; - case "java.lang.Byte": - getAndCastPropertyToWrapper(insn, property, instructions, byte.class, Byte.class); - return instructions; - case "java.lang.Short": - getAndCastPropertyToWrapper(insn, property, instructions, short.class, Short.class); - return instructions; - case "java.lang.Integer": - getAndCastPropertyToWrapper(insn, property, instructions, int.class, Integer.class); - return instructions; - case "java.lang.Float": - getAndCastPropertyToWrapper(insn, property, instructions, float.class, Float.class); - return instructions; - case "java.lang.Double": - getAndCastPropertyToWrapper(insn, property, instructions, double.class, Double.class); - return instructions; - default: { - Variable resultVar = insn.getProgram().createVariable(); - getProperty(insn, property, instructions, resultVar); - CastInstruction castInsn = new CastInstruction(); - castInsn.setReceiver(insn.getReceiver()); - castInsn.setTargetType(type); - castInsn.setValue(resultVar); - instructions.add(castInsn); - return instructions; - } - } - } - return null; - } - - private void getProperty(InvokeInstruction insn, String property, List instructions, - Variable resultVar) { - Program program = insn.getProgram(); - Variable nameVar = program.createVariable(); - StringConstantInstruction nameInsn = new StringConstantInstruction(); - nameInsn.setConstant(property); - nameInsn.setReceiver(nameVar); - instructions.add(nameInsn); - InvokeInstruction accessorInvoke = new InvokeInstruction(); - accessorInvoke.setType(InvocationType.SPECIAL); - accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get", - Object.class, String.class, Object.class)); - accessorInvoke.getArguments().add(insn.getInstance()); - accessorInvoke.getArguments().add(nameVar); - accessorInvoke.setReceiver(resultVar); - instructions.add(accessorInvoke); - } - - private void getAndCastProperty(InvokeInstruction insn, String property, List instructions, - Class primitive) { - Program program = insn.getProgram(); - Variable resultVar = program.createVariable(); - getProperty(insn, property, instructions, resultVar); - InvokeInstruction castInvoke = new InvokeInstruction(); - castInvoke.setType(InvocationType.SPECIAL); - String primitiveCapitalized = primitive.getName(); - primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + - primitiveCapitalized.substring(1); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized, - Object.class, primitive)); - castInvoke.getArguments().add(resultVar); - castInvoke.setReceiver(insn.getReceiver()); - instructions.add(castInvoke); - } - - private void getAndCastPropertyToWrapper(InvokeInstruction insn, String property, List instructions, - Class primitive, Class wrapper) { - Program program = insn.getProgram(); - Variable resultVar = program.createVariable(); - getProperty(insn, property, instructions, resultVar); - InvokeInstruction castInvoke = new InvokeInstruction(); - castInvoke.setType(InvocationType.SPECIAL); - String primitiveCapitalized = primitive.getName(); - primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + - primitiveCapitalized.substring(1); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Wrapper", - Object.class, wrapper)); - castInvoke.getArguments().add(resultVar); - castInvoke.setReceiver(insn.getReceiver()); - instructions.add(castInvoke); - } - - private List transformSetterInvocation(InvokeInstruction insn, String property) { - return null; - } - - private String getPropertyName(String name) { - if (name.length() == 1) { - return name.toLowerCase(); - } - if (Character.isUpperCase(name.charAt(1))) { - return name; - } - return Character.toLowerCase(name.charAt(0)) + name.substring(1); - } } From 0ee5ba52c47837c28e88568d633c6a69d12bc234 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 9 Jun 2014 17:08:49 +0400 Subject: [PATCH 11/16] Removes primitive wrappers support from metadata providers --- .../metadata/DependentTestResource.java | 3 +- .../metadata/MetadataGeneratorTest.java | 17 +-- .../teavm/platform/metadata/TestResource.java | 43 ++----- .../metadata/TestResourceGenerator.java | 24 ++-- .../platform/metadata/BooleanResource.java | 26 +++++ .../teavm/platform/metadata/ByteResource.java | 26 +++++ .../platform/metadata/DoubleResource.java | 26 +++++ .../platform/metadata/FloatResource.java | 26 +++++ .../teavm/platform/metadata/IntResource.java | 26 +++++ .../platform/metadata/MetadataGenerator.java | 6 +- .../metadata/MetadataGeneratorContext.java | 6 +- .../org/teavm/platform/metadata/Resource.java | 8 +- .../platform/metadata/ResourceArray.java | 2 +- .../teavm/platform/metadata/ResourceMap.java | 2 +- .../platform/metadata/ShortResource.java | 26 +++++ .../platform/metadata/StringResource.java | 26 +++++ .../plugin/BuildTimeResourceArray.java | 3 +- .../platform/plugin/BuildTimeResourceMap.java | 3 +- .../plugin/BuildTimeResourceProxyBuilder.java | 9 +- .../DefaultMetadataGeneratorContext.java | 7 +- .../MetadataProviderNativeGenerator.java | 3 +- .../platform/plugin/ResourceAccessor.java | 30 +---- .../ResourceAccessorDependencyListener.java | 47 -------- .../plugin/ResourceAccessorGenerator.java | 58 ---------- .../plugin/ResourceProgramTransformer.java | 106 ------------------ 25 files changed, 235 insertions(+), 324 deletions(-) create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java index 3415d46a1..5b6fbd963 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java @@ -19,8 +19,7 @@ package org.teavm.platform.metadata; * * @author Alexey Andreev */ -@Resource -public interface DependentTestResource { +public interface DependentTestResource extends Resource { String getBar(); void setBar(String bar); diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java index 1c56755aa..ad4bf808a 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -32,11 +32,11 @@ public class MetadataGeneratorTest { } @MetadataProvider(TestResourceGenerator.class) - private native int getInt(); + private native IntResource getInt(); @Test public void intExposed() { - assertEquals(23, getInt()); + assertEquals(23, getInt().getValue()); } @MetadataProvider(TestResourceGenerator.class) @@ -52,20 +52,13 @@ public class MetadataGeneratorTest { assertEquals(3.14, res.getF(), 0.001); assertEquals(2.72, res.getG(), 0.001); - assertEquals(Integer.valueOf(26), res.getH()); - assertNull(res.getI()); - assertEquals(Byte.valueOf((byte)27), res.getJ()); - assertEquals(Short.valueOf((short)28), res.getK()); - assertEquals(100, res.getL().floatValue(), 0.1); - assertEquals(200, res.getM().doubleValue(), 0.1); - assertEquals("qwe", res.getFoo()); assertEquals(2, res.getArrayA().size()); - assertEquals(Integer.valueOf(2), res.getArrayA().get(0)); - assertEquals(Integer.valueOf(3), res.getArrayA().get(1)); + assertEquals(2, res.getArrayA().get(0).getValue()); + assertEquals(3, res.getArrayA().get(1).getValue()); assertEquals(1, res.getArrayB().size()); - assertEquals("baz", res.getArrayB().get(0)); + assertEquals("baz", res.getArrayB().get(0).getBar()); assertNull(res.getArrayC()); } } diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java index d64800bbd..60c4ab2b4 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java @@ -19,8 +19,7 @@ package org.teavm.platform.metadata; * * @author Alexey Andreev */ -@Resource -public interface TestResource { +public interface TestResource extends Resource { int getA(); void setA(int a); @@ -45,55 +44,31 @@ public interface TestResource { void setG(double g); - Integer getH(); - - void setH(Integer a); - - Boolean getI(); - - void setI(Boolean i); - - Byte getJ(); - - void setJ(Byte d); - - Short getK(); - - void setK(Short k); - - Float getL(); - - void setL(Float l); - - Double getM(); - - void setM(Double g); - String getFoo(); void setFoo(String foo); - ResourceArray getArrayA(); + ResourceArray getArrayA(); - void setArrayA(ResourceArray arrayA); + void setArrayA(ResourceArray arrayA); ResourceArray getArrayB(); void setArrayB(ResourceArray arrayB); - ResourceArray> getArrayC(); + ResourceArray> getArrayC(); - void setArrayC(ResourceArray> arrayC); + void setArrayC(ResourceArray> arrayC); - ResourceMap getMapA(); + ResourceMap getMapA(); - void setMapA(ResourceMap mapA); + void setMapA(ResourceMap mapA); ResourceMap getMapB(); void setMapB(ResourceMap mapB); - ResourceMap> getMapC(); + ResourceMap> getMapC(); - void setMapC(ResourceMap> mapC); + void setMapC(ResourceMap> mapC); } diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java index 2dbf1e392..6edf353ce 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java @@ -23,12 +23,12 @@ import org.teavm.model.MethodReference; */ public class TestResourceGenerator implements MetadataGenerator { @Override - public Object generateMetadata(MetadataGeneratorContext context, MethodReference method) { + public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) { switch (method.getName()) { case "getNull": return null; case "getInt": - return 23; + return createInt(context, 23); case "getResource": return getResource(context); default: @@ -36,7 +36,7 @@ public class TestResourceGenerator implements MetadataGenerator { } } - private Object getResource(MetadataGeneratorContext context) { + private Resource getResource(MetadataGeneratorContext context) { TestResource resource = context.createResource(TestResource.class); resource.setA(23); resource.setB(false); @@ -44,17 +44,11 @@ public class TestResourceGenerator implements MetadataGenerator { resource.setE((short)25); resource.setF(3.14f); resource.setG(2.72); - resource.setH(26); - resource.setI(null); - resource.setJ((byte)27); - resource.setK((short)28); - resource.setL(100f); - resource.setM(200.0); resource.setFoo("qwe"); - ResourceArray array = context.createResourceArray(); - array.add(2); - array.add(3); + ResourceArray array = context.createResourceArray(); + array.add(createInt(context, 2)); + array.add(createInt(context, 3)); resource.setArrayA(array); DependentTestResource dep = context.createResource(DependentTestResource.class); dep.setBar("baz"); @@ -63,4 +57,10 @@ public class TestResourceGenerator implements MetadataGenerator { resource.setArrayB(resArray); return resource; } + + private IntResource createInt(MetadataGeneratorContext context, int value) { + IntResource res = context.createResource(IntResource.class); + res.setValue(value); + return res; + } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java new file mode 100644 index 000000000..ebf4bf6f7 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface BooleanResource extends Resource { + boolean getValue(); + + void setValue(boolean value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java new file mode 100644 index 000000000..582cda32b --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface ByteResource extends Resource { + byte getValue(); + + void setValue(byte value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java new file mode 100644 index 000000000..2d728fdf5 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface DoubleResource extends Resource { + double getValue(); + + void setValue(double value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java new file mode 100644 index 000000000..4c159052c --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface FloatResource extends Resource { + float getValue(); + + void setValue(float value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java new file mode 100644 index 000000000..574b84192 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface IntResource extends Resource { + int getValue(); + + void setValue(int 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 8a5a0e918..92c0ce73a 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 @@ -41,12 +41,10 @@ import org.teavm.model.MethodReference; *

The valid resource types are the following:

* *
    - *
  • Valid interfaces, marked with the {@link Resource} annotation. Read the description of this annotation + *
  • Valid interfaces, extending the {@link Resource} annotation. Read the description of this interface * for detailed description about valid resources interfaces.
  • *
  • {@link ResourceArray} of valid resources.
  • *
  • {@link ResourceMap} of valid resources.
  • - *
  • {@link String}, {@link Integer}, {@link Byte}, {@link Short}, {@link Float}, {@link Double}, - * {@link Boolean}.
  • *
  • The null value.
  • *
* @@ -61,5 +59,5 @@ public interface MetadataGenerator { * @param context context that contains useful compile-time information. * @param method method which will be used to access the generated resources at run time. */ - Object generateMetadata(MetadataGeneratorContext context, MethodReference method); + Resource generateMetadata(MetadataGeneratorContext context, MethodReference method); } 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 7003d9344..88f1bca57 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 @@ -45,15 +45,15 @@ public interface MetadataGeneratorContext { * Creates a new resource of the given type. The description of valid resources * is available in documentation for {@link Resource}. */ - T createResource(Class resourceType); + T createResource(Class resourceType); /** * Creates a new resource array. */ - ResourceArray createResourceArray(); + ResourceArray createResourceArray(); /** * Creates a new resource map. */ - ResourceMap createResourceMap(); + ResourceMap createResourceMap(); } diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java index 3524b52d9..b58ee600f 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java @@ -15,8 +15,6 @@ */ package org.teavm.platform.metadata; -import java.lang.annotation.*; - /** *

Marks a valid resource interface. Resource interface is an interface, that has get* and set* methods, * according the default convention for JavaBeans. Each property must have both getter and setter. @@ -28,9 +26,5 @@ import java.lang.annotation.*; * * @author Alexey Andreev */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Inherited -@Documented -public @interface Resource { +public interface Resource { } diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java index 98a85ca2a..059c4ea0d 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java @@ -19,7 +19,7 @@ package org.teavm.platform.metadata; * * @author Alexey Andreev */ -public interface ResourceArray { +public interface ResourceArray extends Resource { int size(); T get(int i); diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java index e798f94cc..3ce3f8bfe 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java @@ -19,7 +19,7 @@ package org.teavm.platform.metadata; * * @author Alexey Andreev */ -public interface ResourceMap { +public interface ResourceMap extends Resource { boolean has(String key); T get(String key); diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java new file mode 100644 index 000000000..c0a31ba68 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface ShortResource extends Resource { + short getValue(); + + void setValue(short value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java new file mode 100644 index 000000000..a48754b30 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.platform.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface StringResource extends Resource { + String getValue(); + + void setValue(String value); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java index 43d1ae312..c2e9a9267 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java @@ -19,13 +19,14 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.teavm.codegen.SourceWriter; +import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.ResourceArray; /** * * @author Alexey Andreev */ -class BuildTimeResourceArray implements ResourceArray, ResourceWriter { +class BuildTimeResourceArray implements ResourceArray, ResourceWriter { private List data = new ArrayList<>(); @Override diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java index 213b2c479..2e1dba77c 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java @@ -19,13 +19,14 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.teavm.codegen.SourceWriter; +import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.ResourceMap; /** * * @author Alexey Andreev */ -class BuildTimeResourceMap implements ResourceMap, ResourceWriter { +class BuildTimeResourceMap implements ResourceMap, ResourceWriter { private Map data = new HashMap<>(); @Override 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 f288a4cfd..9e962512a 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 @@ -29,8 +29,7 @@ import org.teavm.platform.metadata.ResourceMap; class BuildTimeResourceProxyBuilder { private Map, BuildTimeResourceProxyFactory> factories = new HashMap<>(); private static Set> allowedPropertyTypes = new HashSet<>(Arrays.>asList( - boolean.class, Boolean.class, byte.class, Byte.class, short.class, Short.class, - int.class, Integer.class, float.class, Float.class, double.class, Double.class, + boolean.class, byte.class, short.class, int.class, float.class, double.class, String.class, ResourceArray.class, ResourceMap.class)); private static Map, Object> defaultValues = new HashMap<>(); @@ -89,9 +88,9 @@ class BuildTimeResourceProxyBuilder { } private void scanIface(Class iface) { - if (!iface.isAnnotationPresent(Resource.class)) { + if (!Resource.class.isAssignableFrom(iface)) { throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + - ". This type is not marked with the " + Resource.class.getName() + " annotation"); + ". This type does not implement the " + Resource.class.getName() + " interface"); } // Scan methods @@ -136,7 +135,7 @@ class BuildTimeResourceProxyBuilder { String propertyName = property.getKey(); Class propertyType = property.getValue(); if (!allowedPropertyTypes.contains(propertyType)) { - if (!propertyType.isInterface() || !propertyType.isAnnotationPresent(Resource.class)) { + if (!propertyType.isInterface() || !Resource.class.isAssignableFrom(propertyType)) { throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + " has an illegal type " + propertyType.getName()); } 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 cd38cc840..c68f69911 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,6 +19,7 @@ import java.lang.reflect.Proxy; import java.util.Properties; 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; @@ -55,19 +56,19 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { } @Override - public T createResource(Class resourceType) { + public T createResource(Class resourceType) { return resourceType.cast(Proxy.newProxyInstance(classLoader, new Class[] { resourceType, ResourceWriter.class }, proxyBuilder.buildProxy(resourceType))); } @Override - public ResourceArray createResourceArray() { + public ResourceArray createResourceArray() { return new BuildTimeResourceArray<>(); } @Override - public ResourceMap createResourceMap() { + public ResourceMap createResourceMap() { return new BuildTimeResourceMap<>(); } } 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 62497207d..d9801bece 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 @@ -24,6 +24,7 @@ import org.teavm.javascript.ni.GeneratorContext; import org.teavm.model.*; import org.teavm.platform.metadata.MetadataGenerator; import org.teavm.platform.metadata.MetadataProvider; +import org.teavm.platform.metadata.Resource; /** * @@ -73,7 +74,7 @@ public class MetadataProviderNativeGenerator implements Generator { context.getClassLoader(), context.getProperties()); // Generate resource loader - Object resource = generator.generateMetadata(metadataContext, methodRef); + Resource resource = generator.generateMetadata(metadataContext, methodRef); writer.append("if (!window.hasOwnProperty(\"").appendMethodBody(methodRef).append("$$resource\")) {") .indent().softNewLine(); writer.appendMethodBody(methodRef).append("$$resource = "); diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java index 7c399174b..5002ec399 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java @@ -15,6 +15,8 @@ */ package org.teavm.platform.plugin; +import org.teavm.platform.metadata.Resource; + /** * * @author Alexey Andreev @@ -24,9 +26,9 @@ final class ResourceAccessor { public static native void put(Object obj, String propertyName, Object elem); - public static native Object get(Object obj, int index); + public static native Resource get(Object obj, int index); - public static native void add(Object obj, Object elem); + public static native void add(Object obj, Resource elem); public static native boolean has(Object obj, String key); @@ -34,53 +36,29 @@ final class ResourceAccessor { public static native int castToInt(Object obj); - public static native Integer castToIntWrapper(Object obj); - public static native short castToShort(Object obj); - public static native Short castToShortWrapper(Object obj); - public static native byte castToByte(Object obj); - public static native Byte castToByteWrapper(Object obj); - public static native boolean castToBoolean(Object obj); - public static native Boolean castToBooleanWrapper(Object obj); - public static native float castToFloat(Object obj); - public static native Float castToFloatWrapper(Object obj); - public static native double castToDouble(Object obj); - public static native Double castToDoubleWrapper(Object obj); - public static native String castToString(Object obj); public static native Object castFromInt(int value); - public static native Object castFromIntWrapper(Integer value); - public static native Object castFromShort(short value); - public static native Object castFromShortWrapper(Short value); - public static native Object castFromByte(byte value); - public static native Object castFromByteWrapper(Byte value); - public static native Object castFromBoolean(boolean value); - public static native Object castFromBooleanWrapper(Boolean value); - public static native Object castFromFloat(float value); - public static native Object castFromFloatWrapper(Float value); - public static native Object castFromDouble(double value); - public static native Object castFromDoubleWrapper(Double value); - public static native Object castFromString(String value); } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java index 98345dbd1..72c572674 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java @@ -19,7 +19,6 @@ import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyListener; import org.teavm.dependency.FieldDependency; import org.teavm.dependency.MethodDependency; -import org.teavm.model.MethodReference; /** * @@ -37,58 +36,12 @@ class ResourceAccessorDependencyListener implements DependencyListener { @Override public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { - case "castToIntWrapper": - castToWrapper(agent, method, Integer.class, int.class); - break; - case "castToShortWrapper": - castToWrapper(agent, method, Short.class, short.class); - break; - case "castToByteWrapper": - castToWrapper(agent, method, Byte.class, byte.class); - break; - case "castToBooleanWrapper": - castToWrapper(agent, method, Boolean.class, boolean.class); - break; - case "castToFloatWrapper": - castToWrapper(agent, method, Float.class, float.class); - break; - case "castToDoubleWrapper": - castToWrapper(agent, method, Double.class, double.class); - break; - case "castFromIntegerWrapper": - castFromWrapper(agent, method, Integer.class, int.class); - break; - case "castFromShortWrapper": - castFromWrapper(agent, method, Short.class, short.class); - break; - case "castFromByteWrapper": - castFromWrapper(agent, method, Byte.class, byte.class); - break; - case "castFromBooleanWrapper": - castFromWrapper(agent, method, Boolean.class, boolean.class); - break; - case "castFromFloatWrapper": - castFromWrapper(agent, method, Float.class, float.class); - break; - case "castFromDoubleWrapper": - castFromWrapper(agent, method, Double.class, double.class); - break; case "castToString": method.getResult().propagate("java.lang.String"); break; } } - private void castToWrapper(DependencyAgent agent, MethodDependency method, Class wrapper, Class primitive) { - method.getResult().propagate(wrapper.getName()); - agent.linkMethod(new MethodReference(wrapper, "valueOf", primitive, wrapper), method.getStack()).use(); - } - - private void castFromWrapper(DependencyAgent agent, MethodDependency method, Class wrapper, Class primitive) { - String primitiveName = primitive.getName(); - agent.linkMethod(new MethodReference(wrapper, primitiveName + "Value", primitive), method.getStack()).use(); - } - @Override public void fieldAchieved(DependencyAgent agent, FieldDependency field) { } 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 746e3e9b7..73e142c5c 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 @@ -86,42 +86,6 @@ class ResourceAccessorGenerator implements Injector { case "castFromDouble": context.writeExpr(context.getArgument(0)); break; - case "castToIntWrapper": - castToWrapper(context, Integer.class, int.class); - break; - case "castToShortWrapper": - castToWrapper(context, Short.class, short.class); - break; - case "castToByteWrapper": - castToWrapper(context, Byte.class, byte.class); - break; - case "castToBooleanWrapper": - castToWrapper(context, Boolean.class, boolean.class); - break; - case "castToFloatWrapper": - castToWrapper(context, Float.class, float.class); - break; - case "castToDoubleWrapper": - castToWrapper(context, Double.class, double.class); - break; - case "castFromIntWrapper": - castFromWrapper(context, Integer.class, int.class); - break; - case "castFromShortWrapper": - castFromWrapper(context, Short.class, short.class); - break; - case "castFromByteWrapper": - castFromWrapper(context, Byte.class, byte.class); - break; - case "castFromBooleanWrapper": - castFromWrapper(context, Boolean.class, boolean.class); - break; - case "castFromFloatWrapper": - castFromWrapper(context, Float.class, float.class); - break; - case "castFromDoubleWrapper": - castFromWrapper(context, Double.class, double.class); - break; case "castToString": context.getWriter().append("$rt_str("); context.writeExpr(context.getArgument(0)); @@ -135,28 +99,6 @@ class ResourceAccessorGenerator implements Injector { } } - private void castToWrapper(InjectorContext context, Class wrapper, Class primitive) throws IOException { - context.getWriter().append('('); - context.writeExpr(context.getArgument(0)); - context.getWriter().ws().append("==").ws().append("null").ws().append('?').ws().append("null") - .ws().append(':').ws(); - context.getWriter().appendMethodBody(new MethodReference(wrapper, "valueOf", primitive, wrapper)).append('('); - context.writeExpr(context.getArgument(0)); - context.getWriter().append("))"); - } - - private void castFromWrapper(InjectorContext context, Class wrapper, Class primitive) throws IOException { - context.getWriter().append('('); - context.writeExpr(context.getArgument(0)); - context.getWriter().ws().append("==").ws().append("null").ws().append('?').ws().append("null") - .ws().append(':').ws(); - String primitiveName = primitive.getName(); - context.getWriter().appendMethodBody(new MethodReference(wrapper, primitiveName + "Value", primitive)) - .append('('); - context.writeExpr(context.getArgument(0)); - context.getWriter().append("))"); - } - private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException { if (property instanceof ConstantExpr) { String str = (String)((ConstantExpr)property).getValue(); diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index 4b542a767..014433545 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -14,7 +14,6 @@ import org.teavm.platform.metadata.ResourceMap; class ResourceProgramTransformer { private ClassReaderSource innerSource; private Program program; - private Set arrayItemsVars = new HashSet<>(); public ResourceProgramTransformer(ClassReaderSource innerSource, Program program) { this.innerSource = innerSource; @@ -25,11 +24,6 @@ class ResourceProgramTransformer { for (int i = 0; i < program.basicBlockCount(); ++i) { transformBasicBlock(program.basicBlockAt(i)); } - if (!arrayItemsVars.isEmpty()) { - for (int i = 0; i < program.basicBlockCount(); ++i) { - postProcessBasicBlock(program.basicBlockAt(i)); - } - } } private void transformBasicBlock(BasicBlock block) { @@ -48,56 +42,6 @@ class ResourceProgramTransformer { } } - private void postProcessBasicBlock(BasicBlock block) { - List instructions = block.getInstructions(); - for (int i = 0; i < instructions.size(); ++i) { - Instruction insn = instructions.get(i); - if (!(insn instanceof CastInstruction)) { - continue; - } - CastInstruction cast = (CastInstruction)insn; - if (!arrayItemsVars.contains(cast.getReceiver())) { - continue; - } - if (!(cast.getTargetType() instanceof ValueType.Object)) { - continue; - } - String targetTypeName = ((ValueType.Object)cast.getTargetType()).getClassName(); - Variable var = cast.getValue(); - Variable recv = cast.getReceiver(); - switch (targetTypeName) { - case "java.lang.Integer": - instructions.set(i, castToWrapper(var, recv, int.class, Integer.class)); - break; - case "java.lang.Boolean": - instructions.set(i, castToWrapper(var, recv, boolean.class, Boolean.class)); - break; - case "java.lang.Byte": - instructions.set(i, castToWrapper(var, recv, byte.class, Byte.class)); - break; - case "java.lang.Short": - instructions.set(i, castToWrapper(var, recv, short.class, Short.class)); - break; - case "java.lang.Float": - instructions.set(i, castToWrapper(var, recv, float.class, Float.class)); - break; - case "java.lang.Double": - instructions.set(i, castToWrapper(var, recv, double.class, Double.class)); - break; - case "java.lang.String": { - InvokeInstruction castInvoke = new InvokeInstruction(); - castInvoke.setType(InvocationType.SPECIAL); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castToString", - Object.class, String.class)); - castInvoke.getArguments().add(var); - castInvoke.setReceiver(recv); - instructions.set(i, castInvoke); - break; - } - } - } - } - private List transformInvoke(InvokeInstruction insn) { if (insn.getType() != InvocationType.VIRTUAL) { return null; @@ -105,9 +49,6 @@ class ResourceProgramTransformer { MethodReference method = insn.getMethod(); if (method.getClassName().equals(ResourceArray.class.getName()) || method.getClassName().equals(ResourceMap.class.getName())) { - if (method.getName().equals("get")) { - arrayItemsVars.add(insn.getReceiver()); - } InvokeInstruction accessInsn = new InvokeInstruction(); accessInsn.setType(InvocationType.SPECIAL); ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2]; @@ -172,24 +113,6 @@ class ResourceProgramTransformer { } } else if (type instanceof ValueType.Object) { switch (((ValueType.Object)type).getClassName()) { - case "java.lang.Boolean": - getAndCastPropertyToWrapper(insn, property, instructions, boolean.class, Boolean.class); - return instructions; - case "java.lang.Byte": - getAndCastPropertyToWrapper(insn, property, instructions, byte.class, Byte.class); - return instructions; - case "java.lang.Short": - getAndCastPropertyToWrapper(insn, property, instructions, short.class, Short.class); - return instructions; - case "java.lang.Integer": - getAndCastPropertyToWrapper(insn, property, instructions, int.class, Integer.class); - return instructions; - case "java.lang.Float": - getAndCastPropertyToWrapper(insn, property, instructions, float.class, Float.class); - return instructions; - case "java.lang.Double": - getAndCastPropertyToWrapper(insn, property, instructions, double.class, Double.class); - return instructions; case "java.lang.String": { Variable resultVar = insn.getProgram().createVariable(); getProperty(insn, property, instructions, resultVar); @@ -250,35 +173,6 @@ class ResourceProgramTransformer { instructions.add(castInvoke); } - private Instruction castToWrapper(Variable var, Variable receiver, Class primitive, Class wrapper) { - InvokeInstruction castInvoke = new InvokeInstruction(); - castInvoke.setType(InvocationType.SPECIAL); - String primitiveCapitalized = primitive.getName(); - primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + - primitiveCapitalized.substring(1); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Wrapper", - Object.class, wrapper)); - castInvoke.getArguments().add(var); - castInvoke.setReceiver(receiver); - return castInvoke; - } - - private void getAndCastPropertyToWrapper(InvokeInstruction insn, String property, List instructions, - Class primitive, Class wrapper) { - Variable resultVar = program.createVariable(); - getProperty(insn, property, instructions, resultVar); - InvokeInstruction castInvoke = new InvokeInstruction(); - castInvoke.setType(InvocationType.SPECIAL); - String primitiveCapitalized = primitive.getName(); - primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + - primitiveCapitalized.substring(1); - castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized + "Wrapper", - Object.class, wrapper)); - castInvoke.getArguments().add(resultVar); - castInvoke.setReceiver(insn.getReceiver()); - instructions.add(castInvoke); - } - private List transformSetterInvocation(InvokeInstruction insn, String property) { return null; } From b9d6e29ca2ec6cee42bfc831ad04e243589815b6 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 9 Jun 2014 18:52:16 +0400 Subject: [PATCH 12/16] Tests pass --- .../plugin/ResourceProgramTransformer.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index 014433545..798031e2a 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -62,7 +62,7 @@ class ResourceProgramTransformer { return Arrays.asList(accessInsn); } ClassReader iface = innerSource.get(method.getClassName()); - if (iface.getAnnotations().get(Resource.class.getName()) == null) { + if (!isSubclass(iface, Resource.class.getName())) { return null; } if (method.getName().startsWith("get")) { @@ -81,6 +81,23 @@ class ResourceProgramTransformer { return null; } + private boolean isSubclass(ClassReader cls, String superClass) { + if (cls.getName().equals(superClass)) { + return true; + } + ClassReader parent = cls.getParent() != null ? innerSource.get(cls.getParent()) : null; + if (parent != null && isSubclass(parent, superClass)) { + return true; + } + for (String ifaceName : cls.getInterfaces()) { + ClassReader iface = innerSource.get(ifaceName); + if (iface != null) { + return isSubclass(iface, superClass); + } + } + return false; + } + private List transformGetterInvocation(InvokeInstruction insn, String property) { if (insn.getReceiver() == null) { return Collections.emptyList(); From da2a26fb4ff8fc52f8d0aa222531ce78e96a217f Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 9 Jun 2014 19:44:36 +0400 Subject: [PATCH 13/16] Adds more metadata usecases --- .../metadata/MetadataGeneratorTest.java | 39 +++++++++ .../metadata/TestResourceGenerator.java | 2 + .../plugin/BuildTimeResourceProxyBuilder.java | 22 +++++- .../plugin/ResourceAccessorGenerator.java | 12 ++- .../plugin/ResourceProgramTransformer.java | 79 +++++++++++++++++++ 5 files changed, 147 insertions(+), 7 deletions(-) diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java index ad4bf808a..ec59b1299 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -61,5 +61,44 @@ public class MetadataGeneratorTest { assertEquals("baz", res.getArrayB().get(0).getBar()); assertNull(res.getArrayC()); } + + @MetadataProvider(TestResourceGenerator.class) + private native TestResource getEmptyResource(); + + @Test + public void resourceDefaultsSet() { + TestResource res = getEmptyResource(); + assertEquals(0, res.getA()); + assertFalse(res.getB()); + assertEquals(0, res.getD()); + assertEquals(0, res.getE()); + assertEquals(0, res.getF(), 1E-10); + assertEquals(0, res.getG(), 1E-10); + assertNull(res.getFoo()); + assertNull(res.getArrayA()); + assertNull(res.getArrayB()); + assertNull(res.getArrayC()); + assertNull(res.getMapA()); + assertNull(res.getMapB()); + assertNull(res.getMapC()); + } + + @Test + public void resourceModifiedInRunTime() { + TestResource res = getEmptyResource(); + res.setA(23); + res.setB(true); + res.setD((byte)24); + res.setE((short)25); + res.setF(3.14f); + res.setG(2.72); + + assertEquals(23, res.getA()); + assertTrue(res.getB()); + assertEquals(24, res.getD()); + assertEquals(25, res.getE()); + assertEquals(3.14, res.getF(), 0.001); + assertEquals(2.72, res.getG(), 0.001); + } } diff --git a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java index 6edf353ce..43f672805 100644 --- a/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java @@ -31,6 +31,8 @@ public class TestResourceGenerator implements MetadataGenerator { return createInt(context, 23); case "getResource": return getResource(context); + case "getEmptyResource": + return context.createResource(TestResource.class); default: throw new RuntimeException("Unsupported method: " + method); } 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 9e962512a..a936fee0f 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 @@ -62,6 +62,7 @@ class BuildTimeResourceProxyBuilder { Map methods = new HashMap<>(); private Map propertyIndexes = new HashMap<>(); private Object[] initialData; + private Map> propertyTypes = new HashMap<>(); public ProxyFactoryCreation(Class iface) { this.rootIface = iface; @@ -73,12 +74,24 @@ class BuildTimeResourceProxyBuilder { " that is not an interface"); } scanIface(rootIface); + + // Fill default values + initialData = new Object[propertyIndexes.size()]; + for (Map.Entry> property : propertyTypes.entrySet()) { + String propertyName = property.getKey(); + Class propertyType = property.getValue(); + initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType); + } + + // Generate write method Method writeMethod; try { writeMethod = ResourceWriter.class.getMethod("write", SourceWriter.class); } catch (NoSuchMethodException e) { throw new AssertionError("Method must exist", e); } + + // Create factory String[] properties = new String[propertyIndexes.size()]; for (Map.Entry entry : propertyIndexes.entrySet()) { properties[entry.getValue()] = entry.getKey(); @@ -129,18 +142,19 @@ class BuildTimeResourceProxyBuilder { } } - // Verify types of properties and fill default values - initialData = new Object[propertyIndexes.size()]; + // Verify types of properties for (Map.Entry> property : getters.entrySet()) { String propertyName = property.getKey(); Class propertyType = property.getValue(); if (!allowedPropertyTypes.contains(propertyType)) { if (!propertyType.isInterface() || !Resource.class.isAssignableFrom(propertyType)) { - throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + throw new IllegalArgumentException("Property " + rootIface.getName() + "." + propertyName + " has an illegal type " + propertyType.getName()); } } - initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType); + if (!propertyTypes.containsKey(propertyName)) { + propertyTypes.put(propertyName, propertyType); + } } // Scan superinterfaces 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 73e142c5c..1eaa46a52 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 @@ -52,7 +52,7 @@ class ResourceAccessorGenerator implements Injector { context.writeExpr(context.getArgument(0)); writePropertyAccessor(context, context.getArgument(1)); } - context.getWriter().append(']').ws().append('=').ws(); + context.getWriter().ws().append('=').ws(); context.writeExpr(context.getArgument(2)); context.getWriter().append(')'); break; @@ -87,14 +87,20 @@ class ResourceAccessorGenerator implements Injector { context.writeExpr(context.getArgument(0)); break; case "castToString": + context.getWriter().append('('); + context.writeExpr(context.getArgument(0)); + context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws(); context.getWriter().append("$rt_str("); context.writeExpr(context.getArgument(0)); - context.getWriter().append(")"); + context.getWriter().append(")").ws().append(':').ws().append("null)"); break; case "castFromString": + context.getWriter().append('('); + context.writeExpr(context.getArgument(0)); + context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws(); context.getWriter().append("$rt_ustr("); context.writeExpr(context.getArgument(0)); - context.getWriter().append(")"); + context.getWriter().append(")").ws().append(':').ws().append("null)"); break; } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index 798031e2a..56e51343b 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -191,9 +191,88 @@ class ResourceProgramTransformer { } private List transformSetterInvocation(InvokeInstruction insn, String property) { + ValueType type = insn.getMethod().getDescriptor().parameterType(0); + List instructions = new ArrayList<>(); + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case BOOLEAN: + castAndSetProperty(insn, property, instructions, boolean.class); + return instructions; + case BYTE: + castAndSetProperty(insn, property, instructions, byte.class); + return instructions; + case SHORT: + castAndSetProperty(insn, property, instructions, short.class); + return instructions; + case INTEGER: + castAndSetProperty(insn, property, instructions, int.class); + return instructions; + case FLOAT: + castAndSetProperty(insn, property, instructions, float.class); + return instructions; + case DOUBLE: + castAndSetProperty(insn, property, instructions, double.class); + return instructions; + case CHARACTER: + case LONG: + break; + } + } else if (type instanceof ValueType.Object) { + switch (((ValueType.Object)type).getClassName()) { + case "java.lang.String": { + Variable castVar = insn.getProgram().createVariable(); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFromString", + String.class, Object.class)); + castInvoke.getArguments().add(insn.getArguments().get(0)); + castInvoke.setReceiver(castVar); + instructions.add(castInvoke); + setProperty(insn, property, instructions, castVar); + return instructions; + } + default: { + setProperty(insn, property, instructions, insn.getArguments().get(0)); + return instructions; + } + } + } return null; } + private void setProperty(InvokeInstruction insn, String property, List instructions, + Variable valueVar) { + Variable nameVar = program.createVariable(); + StringConstantInstruction nameInsn = new StringConstantInstruction(); + nameInsn.setConstant(property); + nameInsn.setReceiver(nameVar); + instructions.add(nameInsn); + InvokeInstruction accessorInvoke = new InvokeInstruction(); + accessorInvoke.setType(InvocationType.SPECIAL); + accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "put", + Object.class, String.class, Object.class, void.class)); + accessorInvoke.getArguments().add(insn.getInstance()); + accessorInvoke.getArguments().add(nameVar); + accessorInvoke.getArguments().add(valueVar); + instructions.add(accessorInvoke); + } + + private void castAndSetProperty(InvokeInstruction insn, String property, List instructions, + Class primitive) { + Variable castVar = program.createVariable(); + InvokeInstruction castInvoke = new InvokeInstruction(); + castInvoke.setType(InvocationType.SPECIAL); + String primitiveCapitalized = primitive.getName(); + primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) + + primitiveCapitalized.substring(1); + castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFrom" + primitiveCapitalized, + primitive, Object.class)); + castInvoke.getArguments().add(insn.getArguments().get(0)); + castInvoke.setReceiver(castVar); + instructions.add(castInvoke); + setProperty(insn, property, instructions, castVar); + } + private String getPropertyName(String name) { if (name.length() == 1) { return name.toLowerCase(); From c3e30cc295f03d325f6a7756397e8ef171d3c4cf Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 9 Jun 2014 21:26:58 +0400 Subject: [PATCH 14/16] Dependency plugin receives DependencyAgent instead of DependencyChecker --- .../java/lang/CharacterNativeGenerator.java | 4 ++-- .../java/lang/ClassNativeGenerator.java | 6 ++--- .../java/lang/ObjectNativeGenerator.java | 18 +++++---------- .../java/lang/StringNativeGenerator.java | 4 ++-- .../java/lang/SystemNativeGenerator.java | 14 ++++++------ .../lang/reflect/ArrayNativeGenerator.java | 22 ++++++++----------- .../java/util/LocaleNativeGenerator.java | 4 ++-- .../java/util/TimerNativeGenerator.java | 7 +++--- .../teavm/dependency/DependencyPlugin.java | 2 +- .../java/org/teavm/jso/JSNativeGenerator.java | 20 ++++++++--------- 10 files changed, 46 insertions(+), 55 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java index 0a06f086c..6a4470e07 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java @@ -19,7 +19,7 @@ import java.io.IOException; import org.teavm.classlib.impl.unicode.UnicodeHelper; import org.teavm.classlib.impl.unicode.UnicodeSupport; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; @@ -52,7 +52,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "obtainDigitMapping": case "obtainClasses": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java index 88aa1666e..30b71de3b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java @@ -17,7 +17,7 @@ package org.teavm.classlib.java.lang; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; @@ -186,7 +186,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency graph) { + public void methodAchieved(DependencyAgent agent, MethodDependency graph) { switch (graph.getReference().getName()) { case "voidClass": case "booleanClass": @@ -205,7 +205,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug graph.getResult().propagate("java.lang.Class"); break; case "newInstance": - checker.linkMethod(new MethodReference(InstantiationException.class.getName(), "", + agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "", ValueType.VOID), graph.getStack()).use(); break; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index 20a5942c6..64c3c2d1a 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -17,16 +17,12 @@ package org.teavm.classlib.java.lang; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; -import org.teavm.dependency.DependencyPlugin; -import org.teavm.dependency.MethodDependency; +import org.teavm.dependency.*; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.InjectorContext; -import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; /** * @@ -62,13 +58,13 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "clone": method.getVariable(0).connect(method.getResult()); break; case "getClass": - achieveGetClass(checker, method); + achieveGetClass(agent, method); break; case "wrap": method.getVariable(1).connect(method.getResult()); @@ -87,11 +83,9 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu writer.append(".constructor)"); } - private void achieveGetClass(DependencyChecker checker, MethodDependency method) { - String classClass = "java.lang.Class"; - MethodReference initMethod = new MethodReference(classClass, new MethodDescriptor("createNew", - ValueType.object(classClass))); - checker.addEntryPoint(initMethod); + private void achieveGetClass(DependencyAgent agent, MethodDependency method) { + MethodReference initMethod = new MethodReference(Class.class, "createNew", Class.class); + agent.linkMethod(initMethod, method.getStack()).use(); method.getResult().propagate("java.lang.Class"); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java index 34ccf207e..2dec76c28 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java @@ -16,7 +16,7 @@ package org.teavm.classlib.java.lang; import java.io.IOException; -import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Injector; @@ -29,7 +29,7 @@ import org.teavm.model.MethodReference; */ public class StringNativeGenerator implements Injector, DependencyPlugin { @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "wrap": method.getVariable(1).connect(method.getResult()); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java index 81b66a994..78c4de695 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java @@ -54,16 +54,16 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "doArrayCopy": achieveArrayCopy(method); break; case "setOut": - achieveSetOut(checker, method); + achieveSetOut(agent, method); break; case "setErr": - achieveSetErr(checker, method); + achieveSetErr(agent, method); break; } } @@ -97,13 +97,13 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { src.getArrayItem().connect(dest.getArrayItem()); } - private void achieveSetErr(DependencyChecker checker, MethodDependency method) { - FieldDependency fieldDep = checker.linkField(new FieldReference("java.lang.System", "err"), method.getStack()); + private void achieveSetErr(DependencyAgent agent, MethodDependency method) { + FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "err"), method.getStack()); method.getVariable(1).connect(fieldDep.getValue()); } - private void achieveSetOut(DependencyChecker checker, MethodDependency method) { - FieldDependency fieldDep = checker.linkField(new FieldReference("java.lang.System", "out"), method.getStack()); + private void achieveSetOut(DependencyAgent agent, MethodDependency method) { + FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "out"), method.getStack()); method.getVariable(1).connect(fieldDep.getValue()); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index a3b9b6a12..0f65e69ed 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -17,10 +17,7 @@ package org.teavm.classlib.java.lang.reflect; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; -import org.teavm.dependency.DependencyConsumer; -import org.teavm.dependency.DependencyPlugin; -import org.teavm.dependency.MethodDependency; +import org.teavm.dependency.*; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.model.ClassReader; @@ -41,16 +38,16 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "getLength": - achieveGetLength(checker, method); + achieveGetLength(agent, method); break; case "newInstanceImpl": method.getResult().propagate("[java.lang.Object"); break; case "getImpl": - achieveGet(checker, method); + achieveGet(agent, method); break; } } @@ -81,13 +78,12 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { writer.append("return " + array + ".data.length;").softNewLine(); } - private void achieveGetLength(final DependencyChecker checker, final MethodDependency method) { + private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) { method.getVariable(1).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { if (!type.startsWith("[")) { - MethodReference cons = new MethodReference("java.lang.IllegalArgumentException", - new MethodDescriptor("", ValueType.VOID)); - checker.addEntryPoint(cons); + MethodReference cons = new MethodReference(IllegalArgumentException.class, "", void.class); + agent.linkMethod(cons, method.getStack()).use(); } } }); @@ -129,7 +125,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { writer.outdent().append("}").softNewLine(); } - private void achieveGet(final DependencyChecker checker, final MethodDependency method) { + private void achieveGet(final DependencyAgent agent, final MethodDependency method) { method.getVariable(1).getArrayItem().connect(method.getResult()); method.getVariable(1).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { @@ -140,7 +136,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { String wrapper = "java.lang." + primitiveWrappers[i]; MethodReference methodRef = new MethodReference(wrapper, "valueOf", primitiveTypes[i], ValueType.object(wrapper)); - checker.linkMethod(methodRef, method.getStack()).use(); + agent.linkMethod(methodRef, method.getStack()).use(); method.getResult().propagate("java.lang." + primitiveWrappers[i]); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java index 1a8fec60b..b81a1d1c1 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java @@ -17,7 +17,7 @@ package org.teavm.classlib.java.util; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; @@ -63,7 +63,7 @@ public class LocaleNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getMethod().getName()) { case "getDefaultLocale": case "getDisplayCountry": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java index 387f44216..4d9177f5c 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TimerNativeGenerator.java @@ -17,7 +17,7 @@ package org.teavm.classlib.java.util; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; import org.teavm.javascript.ni.Generator; @@ -34,10 +34,11 @@ public class TimerNativeGenerator implements Generator, DependencyPlugin { "performOnce", ValueType.VOID); @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "scheduleOnce": { - MethodDependency performMethod = checker.linkMethod(performOnceRef, method.getStack()); + MethodDependency performMethod = agent.linkMethod(performOnceRef, method.getStack()); + performMethod.use(); method.getVariable(1).connect(performMethod.getVariable(1)); break; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java index a69fa55b2..29d650351 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyPlugin.java @@ -20,5 +20,5 @@ package org.teavm.dependency; * @author Alexey Andreev */ public interface DependencyPlugin { - void methodAchieved(DependencyChecker checker, MethodDependency method); + void methodAchieved(DependencyAgent checker, MethodDependency method); } diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java index cb7e4b2a4..74157561c 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java @@ -17,10 +17,7 @@ package org.teavm.jso; import java.io.IOException; import org.teavm.codegen.SourceWriter; -import org.teavm.dependency.DependencyChecker; -import org.teavm.dependency.DependencyConsumer; -import org.teavm.dependency.DependencyPlugin; -import org.teavm.dependency.MethodDependency; +import org.teavm.dependency.*; import org.teavm.javascript.ast.ConstantExpr; import org.teavm.javascript.ast.Expr; import org.teavm.javascript.ast.InvocationExpr; @@ -28,7 +25,10 @@ import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.InjectorContext; -import org.teavm.model.*; +import org.teavm.model.ClassReader; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; /** * @@ -115,24 +115,24 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin } @Override - public void methodAchieved(final DependencyChecker checker, final MethodDependency method) { + public void methodAchieved(final DependencyAgent agent, final MethodDependency method) { for (int i = 0; i < method.getReference().parameterCount(); ++i) { method.getVariable(i).addConsumer(new DependencyConsumer() { @Override public void consume(String type) { - achieveFunctorMethods(checker, type, method); + achieveFunctorMethods(agent, type, method); } }); } } - private void achieveFunctorMethods(DependencyChecker checker, String type, MethodDependency caller) { + private void achieveFunctorMethods(DependencyAgent agent, String type, MethodDependency caller) { if (caller.isMissing()) { return; } - ClassReader cls = checker.getClassSource().get(type); + ClassReader cls = agent.getClassSource().get(type); if (cls != null) { for (MethodReader method : cls.getMethods()) { - checker.linkMethod(method.getReference(), caller.getStack()).use(); + agent.linkMethod(method.getReference(), caller.getStack()).use(); } } } From 8e521c457fc501ca10fa54224ff8bbfaf10a5623 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 10 Jun 2014 11:10:11 +0400 Subject: [PATCH 15/16] Adds service registration and retrieval infrastructure --- .../org/teavm/common/ServiceRepository.java | 24 ++++++++++++++++ .../org/teavm/dependency/DependencyAgent.java | 3 +- .../teavm/dependency/DependencyChecker.java | 14 ++++++++-- .../java/org/teavm/javascript/Renderer.java | 28 +++++++++++++++++-- .../teavm/javascript/RenderingContext.java | 3 +- .../teavm/javascript/ni/GeneratorContext.java | 3 +- .../teavm/javascript/ni/InjectorContext.java | 6 +++- .../src/main/java/org/teavm/vm/TeaVM.java | 22 +++++++++++++-- .../main/java/org/teavm/vm/spi/TeaVMHost.java | 2 ++ 9 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/common/ServiceRepository.java diff --git a/teavm-core/src/main/java/org/teavm/common/ServiceRepository.java b/teavm-core/src/main/java/org/teavm/common/ServiceRepository.java new file mode 100644 index 000000000..03f706295 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/common/ServiceRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.common; + +/** + * + * @author Alexey Andreev + */ +public interface ServiceRepository { + T getService(Class type); +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java index 2062ef039..c9a7ae198 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java @@ -15,6 +15,7 @@ */ package org.teavm.dependency; +import org.teavm.common.ServiceRepository; import org.teavm.model.ClassHolder; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; @@ -23,7 +24,7 @@ import org.teavm.model.MethodReference; * * @author Alexey Andreev */ -public interface DependencyAgent extends DependencyInfo { +public interface DependencyAgent extends DependencyInfo, ServiceRepository { DependencyNode createNode(); String generateClassName(); diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 4258d3f08..7f3c52976 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -45,18 +45,21 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { private ConcurrentMap achievableClasses = new ConcurrentHashMap<>(); private ConcurrentMap initializedClasses = new ConcurrentHashMap<>(); private List listeners = new ArrayList<>(); + private ServiceRepository services; ConcurrentMap missingMethods = new ConcurrentHashMap<>(); ConcurrentMap missingClasses = new ConcurrentHashMap<>(); ConcurrentMap missingFields = new ConcurrentHashMap<>(); - public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader) { - this(classSource, classLoader, new SimpleFiniteExecutor()); + public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) { + this(classSource, classLoader, services, new SimpleFiniteExecutor()); } - public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, FiniteExecutor executor) { + public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, + FiniteExecutor executor) { this.classSource = new DependencyClassSource(classSource); this.classLoader = classLoader; this.executor = executor; + this.services = services; methodReaderCache = new ConcurrentCachedMapper<>(new Mapper() { @Override public MethodReader map(MethodReference preimage) { return findMethodReader(preimage); @@ -447,4 +450,9 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { sb.append('\n'); } } + + @Override + public T getService(Class type) { + return services.getService(type); + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 1006a503b..ce4d2691e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -22,6 +22,7 @@ import java.util.*; import org.teavm.codegen.NamingException; import org.teavm.codegen.NamingStrategy; import org.teavm.codegen.SourceWriter; +import org.teavm.common.ServiceRepository; import org.teavm.javascript.ast.*; import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.InjectedBy; @@ -44,6 +45,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext private boolean minifying; private Map injectorMap = new HashMap<>(); private Properties properties = new Properties(); + private ServiceRepository services; private static class InjectorHolder { public final Injector injector; @@ -57,11 +59,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext injectorMap.put(method, new InjectorHolder(injector)); } - public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader) { + public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader, + ServiceRepository services) { this.naming = writer.getNaming(); this.writer = writer; this.classSource = classSource; this.classLoader = classLoader; + this.services = services; } @Override @@ -506,7 +510,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext @Override public Properties getProperties() { - return null; + return new Properties(properties); + } + + @Override + public T getService(Class type) { + return services.getService(type); } } @@ -1429,5 +1438,20 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext public int argumentCount() { return arguments.size(); } + + @Override + public T getService(Class type) { + return services.getService(type); + } + + @Override + public Properties getProperties() { + return new Properties(properties); + } + } + + @Override + public T getService(Class type) { + return services.getService(type); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java b/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java index 48e2b6cae..70c374f53 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java @@ -18,13 +18,14 @@ package org.teavm.javascript; import java.util.Properties; import org.teavm.codegen.NamingStrategy; import org.teavm.codegen.SourceWriter; +import org.teavm.common.ServiceRepository; import org.teavm.model.ListableClassReaderSource; /** * * @author Alexey Andreev */ -public interface RenderingContext { +public interface RenderingContext extends ServiceRepository { NamingStrategy getNaming(); SourceWriter getWriter(); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java index d54363239..985727a90 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java @@ -16,13 +16,14 @@ package org.teavm.javascript.ni; import java.util.Properties; +import org.teavm.common.ServiceRepository; import org.teavm.model.ListableClassReaderSource; /** * * @author Alexey Andreev */ -public interface GeneratorContext { +public interface GeneratorContext extends ServiceRepository { String getParameterName(int index); ListableClassReaderSource getClassSource(); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/InjectorContext.java b/teavm-core/src/main/java/org/teavm/javascript/ni/InjectorContext.java index 95cf19776..71db4a2e9 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/InjectorContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/InjectorContext.java @@ -16,7 +16,9 @@ package org.teavm.javascript.ni; import java.io.IOException; +import java.util.Properties; import org.teavm.codegen.SourceWriter; +import org.teavm.common.ServiceRepository; import org.teavm.javascript.ast.Expr; import org.teavm.model.ValueType; @@ -24,7 +26,7 @@ import org.teavm.model.ValueType; * * @author Alexey Andreev */ -public interface InjectorContext { +public interface InjectorContext extends ServiceRepository { Expr getArgument(int index); int argumentCount(); @@ -33,6 +35,8 @@ public interface InjectorContext { SourceWriter getWriter(); + Properties getProperties(); + void writeEscaped(String str) throws IOException; void writeType(ValueType type) throws IOException; diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 8eba7ff75..3a9c78e2a 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -19,6 +19,7 @@ import java.io.*; import java.util.*; import org.teavm.codegen.*; import org.teavm.common.FiniteExecutor; +import org.teavm.common.ServiceRepository; import org.teavm.dependency.*; import org.teavm.javascript.Decompiler; import org.teavm.javascript.Renderer; @@ -65,7 +66,7 @@ import org.teavm.vm.spi.TeaVMPlugin; * * @author Alexey Andreev */ -public class TeaVM implements TeaVMHost { +public class TeaVM implements TeaVMHost, ServiceRepository { private ClassReaderSource classSource; private DependencyChecker dependencyChecker; private FiniteExecutor executor; @@ -78,12 +79,13 @@ public class TeaVM implements TeaVMHost { private Map methodGenerators = new HashMap<>(); private Map methodInjectors = new HashMap<>(); private List rendererListeners = new ArrayList<>(); + private Map, Object> services = new HashMap<>(); private Properties properties = new Properties(); TeaVM(ClassReaderSource classSource, ClassLoader classLoader, FiniteExecutor executor) { this.classSource = classSource; this.classLoader = classLoader; - dependencyChecker = new DependencyChecker(this.classSource, classLoader, executor); + dependencyChecker = new DependencyChecker(this.classSource, classLoader, this, executor); this.executor = executor; } @@ -335,7 +337,7 @@ public class TeaVM implements TeaVMHost { SourceWriterBuilder builder = new SourceWriterBuilder(naming); builder.setMinified(minifying); SourceWriter sourceWriter = builder.build(writer); - Renderer renderer = new Renderer(sourceWriter, classSet, classLoader); + Renderer renderer = new Renderer(sourceWriter, classSet, classLoader, this); for (Map.Entry entry : methodInjectors.entrySet()) { renderer.addInjector(entry.getKey(), entry.getValue()); } @@ -529,4 +531,18 @@ public class TeaVM implements TeaVMHost { plugin.install(this); } } + + @Override + public T getService(Class type) { + Object service = services.get(type); + if (service == null) { + throw new IllegalArgumentException("Service not registered: " + type.getName()); + } + return type.cast(service); + } + + @Override + public void registerService(Class type, T instance) { + services.put(type, instance); + } } diff --git a/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java b/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java index 2acbda0af..59a38ab52 100644 --- a/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java +++ b/teavm-core/src/main/java/org/teavm/vm/spi/TeaVMHost.java @@ -41,6 +41,8 @@ public interface TeaVMHost { void add(RendererListener listener); + void registerService(Class type, T instance); + /** * Gets class loaded that is used by TeaVM. This class loader is usually specified by * {@link TeaVMBuilder#setClassLoader(ClassLoader)} From 5fcd66795e4fd9c8979e374e84b2c2b3ed130a5f Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 10 Jun 2014 11:24:46 +0400 Subject: [PATCH 16/16] Adds service retrieval support in metadata providers --- .../platform/metadata/MetadataGeneratorContext.java | 3 ++- .../plugin/DefaultMetadataGeneratorContext.java | 10 +++++++++- .../plugin/MetadataProviderNativeGenerator.java | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) 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 88f1bca57..067b4fe26 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 @@ -16,6 +16,7 @@ package org.teavm.platform.metadata; import java.util.Properties; +import org.teavm.common.ServiceRepository; import org.teavm.model.ListableClassReaderSource; import org.teavm.vm.TeaVM; @@ -25,7 +26,7 @@ import org.teavm.vm.TeaVM; * * @author Alexey Andreev */ -public interface MetadataGeneratorContext { +public interface MetadataGeneratorContext extends ServiceRepository { /** * Gets the collection of all classes that were achieved by the dependency checker. */ 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 c68f69911..947202715 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 @@ -17,6 +17,7 @@ package org.teavm.platform.plugin; 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; @@ -32,12 +33,14 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { private ClassLoader classLoader; private Properties properties; private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder(); + private ServiceRepository services; public DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, - Properties properties) { + Properties properties, ServiceRepository services) { this.classSource = classSource; this.classLoader = classLoader; this.properties = properties; + this.services = services; } @Override @@ -71,4 +74,9 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { public ResourceMap createResourceMap() { return new BuildTimeResourceMap<>(); } + + @Override + public T getService(Class type) { + return services.getService(type); + } } 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 d9801bece..da0dd8740 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 @@ -71,7 +71,7 @@ public class MetadataProviderNativeGenerator implements Generator { e.getTargetException()); } DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(), - context.getClassLoader(), context.getProperties()); + context.getClassLoader(), context.getProperties(), context); // Generate resource loader Resource resource = generator.generateMetadata(metadataContext, methodRef);