Merge branch 'platform' into date2

Conflicts:
	pom.xml
This commit is contained in:
konsoletyper 2014-06-10 15:42:10 +04:00
commit c4d32fce85
70 changed files with 2609 additions and 86 deletions

View File

@ -77,6 +77,7 @@
<module>teavm-jso</module>
<module>teavm-html4j</module>
<module>teavm-samples</module>
<module>teavm-platform</module>
</modules>
<dependencyManagement>
@ -178,6 +179,11 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
</plugin>
</plugins>
</pluginManagement>
</build>

View File

@ -30,6 +30,12 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-platform</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
@ -51,6 +57,13 @@
<groupId>org.teavm</groupId>
<artifactId>teavm-maven-plugin</artifactId>
<version>${project.version}</version>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-platform</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>generate-javascript-tests</id>
@ -96,6 +109,15 @@
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>org/teavm/platform/metadata/*.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>

View File

@ -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");

View File

@ -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":

View File

@ -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(), "<init>",
agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",
ValueType.VOID), graph.getStack()).use();
break;
}

View File

@ -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");
}

View File

@ -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());

View File

@ -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());
}
}

View File

@ -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("<init>", ValueType.VOID));
checker.addEntryPoint(cons);
MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", 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]);
}
}

View File

@ -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":

View File

@ -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":

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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<IntResource> getArrayA();
void setArrayA(ResourceArray<IntResource> arrayA);
ResourceArray<DependentTestResource> getArrayB();
void setArrayB(ResourceArray<DependentTestResource> arrayB);
ResourceArray<ResourceArray<StringResource>> getArrayC();
void setArrayC(ResourceArray<ResourceArray<StringResource>> arrayC);
ResourceMap<IntResource> getMapA();
void setMapA(ResourceMap<IntResource> mapA);
ResourceMap<DependentTestResource> getMapB();
void setMapB(ResourceMap<DependentTestResource> mapB);
ResourceMap<ResourceArray<StringResource>> getMapC();
void setMapC(ResourceMap<ResourceArray<StringResource>> mapC);
}

View File

@ -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<IntResource> 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<DependentTestResource> 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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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> T getService(Class<T> type);
}

View File

@ -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();

View File

@ -45,18 +45,21 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
private ConcurrentMap<String, Object> achievableClasses = new ConcurrentHashMap<>();
private ConcurrentMap<String, Object> initializedClasses = new ConcurrentHashMap<>();
private List<DependencyListener> listeners = new ArrayList<>();
private ServiceRepository services;
ConcurrentMap<MethodReference, DependencyStack> missingMethods = new ConcurrentHashMap<>();
ConcurrentMap<String, DependencyStack> missingClasses = new ConcurrentHashMap<>();
ConcurrentMap<FieldReference, DependencyStack> 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<MethodReference, MethodReader>() {
@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> T getService(Class<T> type) {
return services.getService(type);
}
}

View File

@ -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);
}

View File

@ -44,6 +44,7 @@ public class Decompiler {
private RangeTree.Node parentNode;
private FiniteExecutor executor;
private Map<MethodReference, Generator> generators = new HashMap<>();
private Set<MethodReference> 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<String> visited, List<String> 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) {

View File

@ -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<MethodReference, InjectorHolder> 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> T getService(Class<T> 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> T getService(Class<T> type) {
return services.getService(type);
}
@Override
public Properties getProperties() {
return new Properties(properties);
}
}
@Override
public <T> T getService(Class<T> type) {
return services.getService(type);
}
}

View File

@ -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 <konsoletyper@gmail.com>
*/
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();
}

View File

@ -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 <konsoletyper@gmail.com>
*/
public interface GeneratorContext {
public interface GeneratorContext extends ServiceRepository {
String getParameterName(int index);
ListableClassReaderSource getClassSource();
ClassLoader getClassLoader();
Properties getProperties();
}

View File

@ -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 <konsoletyper@gmail.com>
*/
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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<Class<?>, 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();

View File

@ -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<String, TeaVMEntryPoint> entryPoints = new HashMap<>();
private Map<String, String> exportedClasses = new HashMap<>();
private Map<MethodReference, Generator> methodGenerators = new HashMap<>();
private Map<MethodReference, Injector> methodInjectors = new HashMap<>();
private List<RendererListener> rendererListeners = new ArrayList<>();
private Map<Class<?>, 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<MethodReference, Generator> entry : methodGenerators.entrySet()) {
decompiler.addGenerator(entry.getKey(), entry.getValue());
}
for (MethodReference injectedMethod : methodInjectors.keySet()) {
decompiler.addMethodToPass(injectedMethod);
}
List<ClassNode> 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<MethodReference, Injector> 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> T getService(Class<T> type) {
Object service = services.get(type);
if (service == null) {
throw new IllegalArgumentException("Service not registered: " + type.getName());
}
return type.cast(service);
}
@Override
public <T> void registerService(Class<T> type, T instance) {
services.put(type, instance);
}
}

View File

@ -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);
<T> void registerService(Class<T> type, T instance);
/**
* Gets class loaded that is used by TeaVM. This class loader is usually specified by
* {@link TeaVMBuilder#setClassLoader(ClassLoader)}

View File

@ -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();
}
}
}

4
teavm-platform/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/target
/.settings
/.project
/.classpath

24
teavm-platform/pom.xml Normal file
View File

@ -0,0 +1,24 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.2-SNAPSHOT</version>
</parent>
<artifactId>teavm-platform</artifactId>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
/**
* <p>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.</p>
*
* <p>Here is the full workflow:</p>
*
* <ul>
* <li>Compiler finds a method that is marked with the {@link MetadataProvider} annotation.
* This method must be declared as <code>native</code>, otherwise compiler should throw an exception.</li>
* <li>Compiler instantiates the {@link MetadataGenerator} instance with the no-arg constructor
* If no such constructor exists, compiler throws exception.</li>
* <li>Compiler runs the {@link #generateMetadata(MetadataGeneratorContext, MethodReference)} method
* ands gets the produced resource.</li>
* <li>Compiler generates implementation of the method marked with {@link MetadataProvider}, that
* will return the generated resource in run time.</li>
* </ul>
*
* <p>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}.</p>
*
* <p>The valid resource types are the following:</p>
*
* <ul>
* <li>Valid interfaces, extending the {@link Resource} annotation. Read the description of this interface
* for detailed description about valid resources interfaces.</li>
* <li>{@link ResourceArray} of valid resources.</li>
* <li>{@link ResourceMap} of valid resources.</li>
* <li>The <code>null</code> value.</li>
* </ul>
*
* <p>All other types are not considered to be resources and therefore are not accepted.</p>
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface MetadataGenerator {
/**
* <p>Generates resources, that will be available at runtime.</p>
*
* @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);
}

View File

@ -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;
/**
* <p>Represents context with compile-time information, that is useful for {@link MetadataGenerator}.
* This context is provided by the compiler infrastructure.</p>
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
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 extends Resource> T createResource(Class<T> resourceType);
/**
* Creates a new resource array.
*/
<T extends Resource> ResourceArray<T> createResourceArray();
/**
* Creates a new resource map.
*/
<T extends Resource> ResourceMap<T> createResourceMap();
}

View File

@ -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;
/**
* <p>Binds a {@link MetadataGenerator} to a native method.</p>
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MetadataProvider {
Class<? extends MetadataGenerator> value();
}

View File

@ -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;
/**
* <p>Marks a valid <b>resource interface</b>. 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 <code>long</code>) or a valid resource.</p>
*
* @see MetadataGenerator
* @see ResourceArray
* @see ResourceMap
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface Resource {
}

View File

@ -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 <konsoletyper@gmail.com>
*/
public interface ResourceArray<T extends Resource> extends Resource {
int size();
T get(int i);
void add(T elem);
}

View File

@ -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 <konsoletyper@gmail.com>
*/
public interface ResourceMap<T extends Resource> extends Resource {
boolean has(String key);
T get(String key);
void put(String key, T value);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<T extends Resource> implements ResourceArray<T>, ResourceWriter {
private List<T> 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();
}
}

View File

@ -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];
}
}

View File

@ -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<T extends Resource> implements ResourceMap<T>, ResourceWriter {
private Map<String, T> 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<String, T> 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();
}
}

View File

@ -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;
}

View File

@ -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 <konsoletyper@gmail.com>
*/
class BuildTimeResourceProxy implements InvocationHandler {
private Map<Method, BuildTimeResourceMethod> methods;
Object[] data;
public BuildTimeResourceProxy(Map<Method, BuildTimeResourceMethod> 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);
}
}

View File

@ -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<Class<?>, BuildTimeResourceProxyFactory> factories = new HashMap<>();
private static Set<Class<?>> allowedPropertyTypes = new HashSet<>(Arrays.<Class<?>>asList(
boolean.class, byte.class, short.class, int.class, float.class, double.class,
String.class, ResourceArray.class, ResourceMap.class));
private static Map<Class<?>, 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<String, Class<?>> getters = new HashMap<>();
Map<String, Class<?>> setters = new HashMap<>();
Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
private Map<String, Integer> propertyIndexes = new HashMap<>();
private Object[] initialData;
private Map<String, Class<?>> 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<String, Class<?>> 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<String, Integer> 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<String, Class<?>> 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<String, Class<?>> 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;
}
}
}

View File

@ -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<Method, BuildTimeResourceMethod> methods = new HashMap<>();
private Object[] initialData;
public BuildTimeResourceProxyFactory(Map<Method, BuildTimeResourceMethod> methods, Object[] initialData) {
this.methods = methods;
this.initialData = initialData;
}
BuildTimeResourceProxy create() {
return new BuildTimeResourceProxy(methods, initialData);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 extends Resource> T createResource(Class<T> resourceType) {
return resourceType.cast(Proxy.newProxyInstance(classLoader,
new Class<?>[] { resourceType, ResourceWriter.class },
proxyBuilder.buildProxy(resourceType)));
}
@Override
public <T extends Resource> ResourceArray<T> createResourceArray() {
return new BuildTimeResourceArray<>();
}
@Override
public <T extends Resource> ResourceMap<T> createResourceMap() {
return new BuildTimeResourceMap<>();
}
@Override
public <T> T getService(Class<T> type) {
return services.getService(type);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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 <konsoletyper@gmail.com>
*/
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());
}
}

View File

@ -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);
}

View File

@ -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) {
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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<Instruction> 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<Instruction> replacement = transformInvoke(invoke);
if (replacement != null) {
instructions.set(i, new EmptyInstruction());
instructions.addAll(i, replacement);
i += replacement.size();
}
}
}
}
private List<Instruction> 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.<Instruction>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<Instruction> transformGetterInvocation(InvokeInstruction insn, String property) {
if (insn.getReceiver() == null) {
return Collections.emptyList();
}
ValueType type = insn.getMethod().getDescriptor().getResultType();
List<Instruction> 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<Instruction> 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<Instruction> 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<Instruction> transformSetterInvocation(InvokeInstruction insn, String property) {
ValueType type = insn.getMethod().getDescriptor().parameterType(0);
List<Instruction> 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<Instruction> 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<Instruction> 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);
}
}

View File

@ -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();
}
}
}
}

View File

@ -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;
}

View File

@ -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('"');
}
}

View File

@ -0,0 +1 @@
org.teavm.platform.plugin.PlatformPlugin