diff --git a/pom.xml b/pom.xml index 3c262e655..d8bdf4a45 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ teavm-jso teavm-html4j teavm-samples + teavm-platform @@ -178,6 +179,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-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java index 7254e7059..63ec3fd5d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java @@ -17,7 +17,7 @@ package org.teavm.classlib.impl.unicode; 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; @@ -30,7 +30,7 @@ import org.teavm.model.MethodReference; */ public class CLDRHelperNativeGenerator implements Generator, DependencyPlugin { @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getMethod().getName()) { case "getLikelySubtagsImpl": method.getResult().propagate("java.lang.String"); 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/DateNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java index ce80ccb3c..16529f4b7 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.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; @@ -70,7 +70,7 @@ public class DateNativeGenerator implements Generator, DependencyPlugin { } @Override - public void methodAchieved(DependencyChecker checker, MethodDependency method) { + public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getMethod().getName()) { case "toString": case "toLocaleFormat": 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 9e7ab52a3..ff0d18604 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; @@ -67,7 +67,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-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.java new file mode 100644 index 000000000..5b6fbd963 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/DependentTestResource.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 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 new file mode 100644 index 000000000..ec59b1299 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/MetadataGeneratorTest.java @@ -0,0 +1,104 @@ +/* + * 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 IntResource getInt(); + + @Test + public void intExposed() { + assertEquals(23, getInt().getValue()); + } + + @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("qwe", res.getFoo()); + + assertEquals(2, res.getArrayA().size()); + 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).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/TestResource.java b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.java new file mode 100644 index 000000000..60c4ab2b4 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResource.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.metadata; + +/** + * + * @author Alexey Andreev + */ +public interface TestResource extends Resource { + 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); + + 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..43f672805 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/platform/metadata/TestResourceGenerator.java @@ -0,0 +1,68 @@ +/* + * 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 Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) { + switch (method.getName()) { + case "getNull": + return null; + case "getInt": + return createInt(context, 23); + case "getResource": + return getResource(context); + case "getEmptyResource": + return context.createResource(TestResource.class); + default: + throw new RuntimeException("Unsupported method: " + method); + } + } + + private Resource 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.setFoo("qwe"); + + 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"); + ResourceArray resArray = context.createResourceArray(); + resArray.add(dep); + 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-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/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/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-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index 6630b8362..9a0e8078e 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; @@ -84,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))); } }); @@ -96,6 +98,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 +131,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.getReference())) { continue; } MethodNode methodNode = decompile(method); @@ -140,8 +147,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 8fef2ed31..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; @@ -43,6 +44,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext private ClassLoader classLoader; private boolean minifying; private Map injectorMap = new HashMap<>(); + private Properties properties = new Properties(); + private ServiceRepository services; private static class InjectorHolder { public final Injector injector; @@ -52,11 +55,17 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } } - public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader) { + public void addInjector(MethodReference method, Injector injector) { + injectorMap.put(method, new InjectorHolder(injector)); + } + + 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 @@ -88,6 +97,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 +502,21 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext public ListableClassReaderSource getClassSource() { return classSource; } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public Properties getProperties() { + return new Properties(properties); + } + + @Override + public T getService(Class type) { + return services.getService(type); + } } @Override @@ -1404,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 2b15f5684..70c374f53 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/RenderingContext.java @@ -15,15 +15,17 @@ */ 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(); @@ -33,4 +35,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..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 @@ -15,14 +15,20 @@ */ 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(); + + ClassLoader getClassLoader(); + + Properties getProperties(); } 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/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..53547517b 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); } @@ -175,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) { @@ -267,6 +302,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..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,10 +19,14 @@ 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.*; +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; @@ -62,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; @@ -73,13 +77,15 @@ 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 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; } @@ -98,6 +104,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 +326,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 @@ -323,7 +337,10 @@ 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()); + } try { for (RendererListener listener : rendererListeners) { listener.begin(renderer, target); @@ -514,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 fc0f2cfbc..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 @@ -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,8 +37,12 @@ public interface TeaVMHost { void add(MethodReference methodRef, Generator generator); + void add(MethodReference methodRef, Injector injector); + 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)} 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(); } } } 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..a4fd22179 --- /dev/null +++ b/teavm-platform/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + + org.teavm + teavm + 0.2-SNAPSHOT + + teavm-platform + + + + org.teavm + 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/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 new file mode 100644 index 000000000..92c0ce73a --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java @@ -0,0 +1,63 @@ +/* + * 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, 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.
  • + *
  • 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. + */ + 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 new file mode 100644 index 000000000..067b4fe26 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java @@ -0,0 +1,60 @@ +/* + * 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.common.ServiceRepository; +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 extends ServiceRepository { + /** + * 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 new file mode 100644 index 000000000..7065725d0 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java @@ -0,0 +1,32 @@ +/* + * 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; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

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

+ * + * @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..b58ee600f --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.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.metadata; + +/** + *

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 a valid resource.

+ * + * @see MetadataGenerator + * @see ResourceArray + * @see ResourceMap + * + * @author Alexey Andreev + */ +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..059c4ea0d --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java @@ -0,0 +1,28 @@ +/* + * 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 ResourceArray extends Resource { + 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..3ce3f8bfe --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java @@ -0,0 +1,28 @@ +/* + * 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 ResourceMap extends Resource { + 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/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 new file mode 100644 index 000000000..c2e9a9267 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java @@ -0,0 +1,58 @@ +/* + * 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 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 { + 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); + } + + @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/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..2e1dba77c --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java @@ -0,0 +1,62 @@ +/* + * 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 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 { + 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); + } + + @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/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..a936fee0f --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java @@ -0,0 +1,238 @@ +/* + * 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.codegen.SourceWriter; +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, byte.class, short.class, int.class, float.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); + 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 Map propertyIndexes = new HashMap<>(); + private Object[] initialData; + private Map> propertyTypes = 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); + + // 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(); + } + methods.put(writeMethod, new BuildTimeResourceWriterMethod(properties)); + return new BuildTimeResourceProxyFactory(methods, initialData); + } + + private void scanIface(Class iface) { + if (!Resource.class.isAssignableFrom(iface)) { + throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + + ". This type does not implement the " + Resource.class.getName() + " interface"); + } + + // 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 + 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 " + rootIface.getName() + "." + propertyName + + " has an illegal type " + propertyType.getName()); + } + } + if (!propertyTypes.containsKey(propertyName)) { + propertyTypes.put(propertyName, propertyType); + } + } + + // 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(3)); + 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/BuildTimeResourceSetter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java new file mode 100644 index 000000000..e8caaa40d --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java @@ -0,0 +1,34 @@ +/* + * 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 BuildTimeResourceSetter implements BuildTimeResourceMethod { + private int index; + + public BuildTimeResourceSetter(int index) { + this.index = index; + } + + @Override + 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/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 new file mode 100644 index 000000000..947202715 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java @@ -0,0 +1,82 @@ +/* + * 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.common.ServiceRepository; +import org.teavm.model.ListableClassReaderSource; +import org.teavm.platform.metadata.MetadataGeneratorContext; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; + +/** + * + * @author Alexey Andreev + */ +class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { + private ListableClassReaderSource classSource; + private ClassLoader classLoader; + private Properties properties; + private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder(); + private ServiceRepository services; + + public DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, + Properties properties, ServiceRepository services) { + this.classSource = classSource; + this.classLoader = classLoader; + this.properties = properties; + this.services = services; + } + + @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, ResourceWriter.class }, + proxyBuilder.buildProxy(resourceType))); + } + + @Override + public ResourceArray createResourceArray() { + return new BuildTimeResourceArray<>(); + } + + @Override + 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 new file mode 100644 index 000000000..da0dd8740 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.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; + +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; +import org.teavm.platform.metadata.Resource; + +/** + * + * @author Alexey Andreev + */ +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()); + 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"); + } + + // 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(), context); + + // Generate resource loader + Resource resource = generator.generateMetadata(metadataContext, methodRef); + writer.append("if (!window.hasOwnProperty(\"").appendMethodBody(methodRef).append("$$resource\")) {") + .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/MetadataProviderTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java new file mode 100644 index 000000000..f8d25aadf --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.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 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) { + continue; + } + AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName()); + genAnnot.getValues().put("value", new AnnotationValue(ValueType.object( + MetadataProviderNativeGenerator.class.getName()))); + method.getAnnotations().add(genAnnot); + } + } +} diff --git a/teavm-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..ad16bfc25 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.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; + +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) { + 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..5002ec399 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java @@ -0,0 +1,64 @@ +/* + * 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.platform.metadata.Resource; + +/** + * + * @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 Resource get(Object obj, int index); + + public static native void add(Object obj, Resource elem); + + public static native boolean has(Object obj, String key); + + public static native int size(Object obj); + + public static native int castToInt(Object obj); + + public static native short castToShort(Object obj); + + public static native byte castToByte(Object obj); + + public static native boolean castToBoolean(Object obj); + + public static native float castToFloat(Object obj); + + public static native double castToDouble(Object obj); + + public static native String castToString(Object obj); + + public static native Object castFromInt(int value); + + public static native Object castFromShort(short value); + + public static native Object castFromByte(byte value); + + public static native Object castFromBoolean(boolean value); + + public static native Object castFromFloat(float value); + + public static native Object castFromDouble(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..72c572674 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * + * @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 "castToString": + method.getResult().propagate("java.lang.String"); + break; + } + } + + @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..1eaa46a52 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java @@ -0,0 +1,149 @@ +/* + * 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(0)); + context.getWriter().append('['); + context.writeExpr(context.getArgument(1)); + context.getWriter().append(']'); + } else { + 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(0)); + context.getWriter().append('['); + context.writeExpr(context.getArgument(1)); + } else { + context.writeExpr(context.getArgument(0)); + writePropertyAccessor(context, context.getArgument(1)); + } + context.getWriter().ws().append('=').ws(); + context.writeExpr(context.getArgument(2)); + context.getWriter().append(')'); + break; + 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(1)); + context.getWriter().append(')'); + break; + case "size": + context.writeExpr(context.getArgument(0)); + 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(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(")").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(")").ws().append(':').ws().append("null)"); + break; + } + } + + 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..6a3819013 --- /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) { + 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/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java new file mode 100644 index 000000000..56e51343b --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -0,0 +1,285 @@ +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; + + 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)); + } + } + + 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 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())) { + 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 (!isSubclass(iface, Resource.class.getName())) { + 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 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(); + } + 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.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 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(); + } + 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 new file mode 100644 index 000000000..f0b8854d2 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java @@ -0,0 +1,34 @@ +/* + * 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.*; + +/** + * + * @author Alexey Andreev + */ +class ResourceTransformer implements ClassHolderTransformer { + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { + for (MethodHolder method : cls.getMethods()) { + Program program = method.getProgram(); + if (program != null) { + new ResourceProgramTransformer(innerSource, program).transformProgram(); + } + } + } +} 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..b457a5d70 --- /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 + */ +public 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('"'); + } +} 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