type, T instance);
+
/**
* Gets class loaded that is used by TeaVM. This class loader is usually specified by
* {@link TeaVMBuilder#setClassLoader(ClassLoader)}
diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java
index cb7e4b2a4..74157561c 100644
--- a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java
+++ b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java
@@ -17,10 +17,7 @@ package org.teavm.jso;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
-import org.teavm.dependency.DependencyChecker;
-import org.teavm.dependency.DependencyConsumer;
-import org.teavm.dependency.DependencyPlugin;
-import org.teavm.dependency.MethodDependency;
+import org.teavm.dependency.*;
import org.teavm.javascript.ast.ConstantExpr;
import org.teavm.javascript.ast.Expr;
import org.teavm.javascript.ast.InvocationExpr;
@@ -28,7 +25,10 @@ import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.javascript.ni.Injector;
import org.teavm.javascript.ni.InjectorContext;
-import org.teavm.model.*;
+import org.teavm.model.ClassReader;
+import org.teavm.model.FieldReference;
+import org.teavm.model.MethodReader;
+import org.teavm.model.MethodReference;
/**
*
@@ -115,24 +115,24 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin
}
@Override
- public void methodAchieved(final DependencyChecker checker, final MethodDependency method) {
+ public void methodAchieved(final DependencyAgent agent, final MethodDependency method) {
for (int i = 0; i < method.getReference().parameterCount(); ++i) {
method.getVariable(i).addConsumer(new DependencyConsumer() {
@Override public void consume(String type) {
- achieveFunctorMethods(checker, type, method);
+ achieveFunctorMethods(agent, type, method);
}
});
}
}
- private void achieveFunctorMethods(DependencyChecker checker, String type, MethodDependency caller) {
+ private void achieveFunctorMethods(DependencyAgent agent, String type, MethodDependency caller) {
if (caller.isMissing()) {
return;
}
- ClassReader cls = checker.getClassSource().get(type);
+ ClassReader cls = agent.getClassSource().get(type);
if (cls != null) {
for (MethodReader method : cls.getMethods()) {
- checker.linkMethod(method.getReference(), caller.getStack()).use();
+ agent.linkMethod(method.getReference(), caller.getStack()).use();
}
}
}
diff --git a/teavm-platform/.gitignore b/teavm-platform/.gitignore
new file mode 100644
index 000000000..bb138cf03
--- /dev/null
+++ b/teavm-platform/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.settings
+/.project
+/.classpath
diff --git a/teavm-platform/pom.xml b/teavm-platform/pom.xml
new file mode 100644
index 000000000..a4fd22179
--- /dev/null
+++ b/teavm-platform/pom.xml
@@ -0,0 +1,24 @@
+
+ 4.0.0
+
+
+ org.teavm
+ teavm
+ 0.2-SNAPSHOT
+
+ teavm-platform
+
+
+
+ org.teavm
+ teavm-core
+ ${project.version}
+
+
+ junit
+ junit
+ test
+
+
+
\ No newline at end of file
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java
new file mode 100644
index 000000000..ebf4bf6f7
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/BooleanResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface BooleanResource extends Resource {
+ boolean getValue();
+
+ void setValue(boolean value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java
new file mode 100644
index 000000000..582cda32b
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ByteResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface ByteResource extends Resource {
+ byte getValue();
+
+ void setValue(byte value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java
new file mode 100644
index 000000000..2d728fdf5
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/DoubleResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface DoubleResource extends Resource {
+ double getValue();
+
+ void setValue(double value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java
new file mode 100644
index 000000000..4c159052c
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/FloatResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface FloatResource extends Resource {
+ float getValue();
+
+ void setValue(float value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java
new file mode 100644
index 000000000..574b84192
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/IntResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface IntResource extends Resource {
+ int getValue();
+
+ void setValue(int value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java
new file mode 100644
index 000000000..92c0ce73a
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGenerator.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+import org.teavm.model.MethodReference;
+
+/**
+ * Represents a generator, that produces resources during compilation phase. User must implement this
+ * interface and bind this implementation to a method that would read resources at runtime.
+ *
+ * Here is the full workflow:
+ *
+ *
+ * - Compiler finds a method that is marked with the {@link MetadataProvider} annotation.
+ * This method must be declared as
native
, otherwise compiler should throw an exception.
+ * - Compiler instantiates the {@link MetadataGenerator} instance with the no-arg constructor
+ * If no such constructor exists, compiler throws exception.
+ * - Compiler runs the {@link #generateMetadata(MetadataGeneratorContext, MethodReference)} method
+ * ands gets the produced resource.
+ * - Compiler generates implementation of the method marked with {@link MetadataProvider}, that
+ * will return the generated resource in run time.
+ *
+ *
+ * Therefore, the type of the value, returned by the
+ * {@link #generateMetadata(MetadataGeneratorContext, MethodReference)}
+ * method must match the returning type of the appropriate method, marked with {@link MetadataProvider}.
+ *
+ * The valid resource types are the following:
+ *
+ *
+ * - Valid interfaces, extending the {@link Resource} annotation. Read the description of this interface
+ * for detailed description about valid resources interfaces.
+ * - {@link ResourceArray} of valid resources.
+ * - {@link ResourceMap} of valid resources.
+ * - The
null
value.
+ *
+ *
+ * All other types are not considered to be resources and therefore are not accepted.
+ *
+ * @author Alexey Andreev
+ */
+public interface MetadataGenerator {
+ /**
+ * Generates resources, that will be available at runtime.
+ *
+ * @param context context that contains useful compile-time information.
+ * @param method method which will be used to access the generated resources at run time.
+ */
+ Resource generateMetadata(MetadataGeneratorContext context, MethodReference method);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java
new file mode 100644
index 000000000..067b4fe26
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+import java.util.Properties;
+import org.teavm.common.ServiceRepository;
+import org.teavm.model.ListableClassReaderSource;
+import org.teavm.vm.TeaVM;
+
+/**
+ * Represents context with compile-time information, that is useful for {@link MetadataGenerator}.
+ * This context is provided by the compiler infrastructure.
+ *
+ * @author Alexey Andreev
+ */
+public interface MetadataGeneratorContext extends ServiceRepository {
+ /**
+ * Gets the collection of all classes that were achieved by the dependency checker.
+ */
+ ListableClassReaderSource getClassSource();
+
+ /**
+ * Gets the class loader that is used by the compiler.
+ */
+ ClassLoader getClassLoader();
+
+ /**
+ * Gets properties that were specified to {@link TeaVM}.
+ */
+ Properties getProperties();
+
+ /**
+ * Creates a new resource of the given type. The description of valid resources
+ * is available in documentation for {@link Resource}.
+ */
+ T createResource(Class resourceType);
+
+ /**
+ * Creates a new resource array.
+ */
+ ResourceArray createResourceArray();
+
+ /**
+ * Creates a new resource map.
+ */
+ ResourceMap createResourceMap();
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java
new file mode 100644
index 000000000..7065725d0
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/MetadataProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Binds a {@link MetadataGenerator} to a native method.
+ *
+ * @author Alexey Andreev
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface MetadataProvider {
+ Class extends MetadataGenerator> value();
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java
new file mode 100644
index 000000000..b58ee600f
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/Resource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ * Marks a valid resource interface. Resource interface is an interface, that has get* and set* methods,
+ * according the default convention for JavaBeans. Each property must have both getter and setter.
+ * Also each property's must be either primitive value (except for long
) or a valid resource.
+ *
+ * @see MetadataGenerator
+ * @see ResourceArray
+ * @see ResourceMap
+ *
+ * @author Alexey Andreev
+ */
+public interface Resource {
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java
new file mode 100644
index 000000000..059c4ea0d
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceArray.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface ResourceArray extends Resource {
+ int size();
+
+ T get(int i);
+
+ void add(T elem);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java
new file mode 100644
index 000000000..3ce3f8bfe
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ResourceMap.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface ResourceMap extends Resource {
+ boolean has(String key);
+
+ T get(String key);
+
+ void put(String key, T value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java
new file mode 100644
index 000000000..c0a31ba68
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/ShortResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface ShortResource extends Resource {
+ short getValue();
+
+ void setValue(short value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java b/teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java
new file mode 100644
index 000000000..a48754b30
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/metadata/StringResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.metadata;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface StringResource extends Resource {
+ String getValue();
+
+ void setValue(String value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java
new file mode 100644
index 000000000..c2e9a9267
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.platform.metadata.Resource;
+import org.teavm.platform.metadata.ResourceArray;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceArray implements ResourceArray, ResourceWriter {
+ private List data = new ArrayList<>();
+
+ @Override
+ public int size() {
+ return data.size();
+ }
+
+ @Override
+ public T get(int i) {
+ return data.get(i);
+ }
+
+ @Override
+ public void add(T elem) {
+ data.add(elem);
+ }
+
+ @Override
+ public void write(SourceWriter writer) throws IOException {
+ writer.append('[').tokenBoundary();
+ for (int i = 0; i < data.size(); ++i) {
+ if (i > 0) {
+ writer.append(',').ws();
+ }
+ ResourceWriterHelper.write(writer, data.get(i));
+ }
+ writer.append(']').tokenBoundary();
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java
new file mode 100644
index 000000000..f00aa2e27
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceGetter implements BuildTimeResourceMethod {
+ private int index;
+
+ public BuildTimeResourceGetter(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable {
+ return proxy.data[index];
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java
new file mode 100644
index 000000000..2e1dba77c
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.platform.metadata.Resource;
+import org.teavm.platform.metadata.ResourceMap;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceMap implements ResourceMap, ResourceWriter {
+ private Map data = new HashMap<>();
+
+ @Override
+ public boolean has(String key) {
+ return data.containsKey(key);
+ }
+
+ @Override
+ public T get(String key) {
+ return data.get(key);
+ }
+
+ @Override
+ public void put(String key, T value) {
+ data.put(key, value);
+ }
+
+ @Override
+ public void write(SourceWriter writer) throws IOException {
+ writer.append('{');
+ boolean first = true;
+ for (Map.Entry entry : data.entrySet()) {
+ if (!first) {
+ writer.append(",").ws();
+ }
+ first = false;
+ ResourceWriterHelper.writeString(writer, entry.getKey());
+ writer.ws().append(':').ws();
+ ResourceWriterHelper.write(writer, entry.getValue());
+ }
+ writer.append('}').tokenBoundary();
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java
new file mode 100644
index 000000000..31f3be541
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMethod.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+interface BuildTimeResourceMethod {
+ Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable;
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java
new file mode 100644
index 000000000..390acb1e5
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceProxy implements InvocationHandler {
+ private Map methods;
+ Object[] data;
+
+ public BuildTimeResourceProxy(Map methods, Object[] initialData) {
+ this.methods = methods;
+ data = Arrays.copyOf(initialData, initialData.length);
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return methods.get(method).invoke(this, args);
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java
new file mode 100644
index 000000000..a936fee0f
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.lang.reflect.Method;
+import java.util.*;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.platform.metadata.Resource;
+import org.teavm.platform.metadata.ResourceArray;
+import org.teavm.platform.metadata.ResourceMap;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceProxyBuilder {
+ private Map, BuildTimeResourceProxyFactory> factories = new HashMap<>();
+ private static Set> allowedPropertyTypes = new HashSet<>(Arrays.>asList(
+ boolean.class, byte.class, short.class, int.class, float.class, double.class,
+ String.class, ResourceArray.class, ResourceMap.class));
+ private static Map, Object> defaultValues = new HashMap<>();
+
+ static {
+ defaultValues.put(boolean.class, false);
+ defaultValues.put(byte.class, (byte)0);
+ defaultValues.put(short.class, (short)0);
+ defaultValues.put(int.class, 0);
+ defaultValues.put(float.class, 0F);
+ defaultValues.put(double.class, 0.0);
+ }
+
+ public BuildTimeResourceProxy buildProxy(Class> iface) {
+ BuildTimeResourceProxyFactory factory = factories.get(iface);
+ if (factory == null) {
+ factory = createFactory(iface);
+ factories.put(iface, factory);
+ }
+ return factory.create();
+ }
+
+ private BuildTimeResourceProxyFactory createFactory(Class> iface) {
+ return new ProxyFactoryCreation(iface).create();
+ }
+
+ private static class ProxyFactoryCreation {
+ private Class> rootIface;
+ Map> getters = new HashMap<>();
+ Map> setters = new HashMap<>();
+ Map methods = new HashMap<>();
+ private Map propertyIndexes = new HashMap<>();
+ private Object[] initialData;
+ private Map> propertyTypes = new HashMap<>();
+
+ public ProxyFactoryCreation(Class> iface) {
+ this.rootIface = iface;
+ }
+
+ BuildTimeResourceProxyFactory create() {
+ if (!rootIface.isInterface()) {
+ throw new IllegalArgumentException("Error creating a new resource of type " + rootIface.getName() +
+ " that is not an interface");
+ }
+ scanIface(rootIface);
+
+ // Fill default values
+ initialData = new Object[propertyIndexes.size()];
+ for (Map.Entry> property : propertyTypes.entrySet()) {
+ String propertyName = property.getKey();
+ Class> propertyType = property.getValue();
+ initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType);
+ }
+
+ // Generate write method
+ Method writeMethod;
+ try {
+ writeMethod = ResourceWriter.class.getMethod("write", SourceWriter.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError("Method must exist", e);
+ }
+
+ // Create factory
+ String[] properties = new String[propertyIndexes.size()];
+ for (Map.Entry entry : propertyIndexes.entrySet()) {
+ properties[entry.getValue()] = entry.getKey();
+ }
+ methods.put(writeMethod, new BuildTimeResourceWriterMethod(properties));
+ return new BuildTimeResourceProxyFactory(methods, initialData);
+ }
+
+ private void scanIface(Class> iface) {
+ if (!Resource.class.isAssignableFrom(iface)) {
+ throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() +
+ ". This type does not implement the " + Resource.class.getName() + " interface");
+ }
+
+ // Scan methods
+ getters.clear();
+ setters.clear();
+ for (Method method : iface.getDeclaredMethods()) {
+ if (method.getName().startsWith("get")) {
+ scanGetter(method);
+ } else if (method.getName().startsWith("is")) {
+ scanBooleanGetter(method);
+ } else if (method.getName().startsWith("set")) {
+ scanSetter(method);
+ } else {
+ throwInvalidMethod(method);
+ }
+ }
+
+ // Verify consistency of getters and setters
+ for (Map.Entry> property : getters.entrySet()) {
+ String propertyName = property.getKey();
+ Class> getterType = property.getValue();
+ Class> setterType = setters.get(propertyName);
+ if (setterType == null) {
+ throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName +
+ " has a getter, but does not have a setter");
+ }
+ if (!setterType.equals(getterType)) {
+ throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName +
+ " has a getter and a setter of different types");
+ }
+ }
+ for (String propertyName : setters.keySet()) {
+ if (!getters.containsKey(propertyName)) {
+ throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName +
+ " has a setter, but does not have a getter");
+ }
+ }
+
+ // Verify types of properties
+ for (Map.Entry> property : getters.entrySet()) {
+ String propertyName = property.getKey();
+ Class> propertyType = property.getValue();
+ if (!allowedPropertyTypes.contains(propertyType)) {
+ if (!propertyType.isInterface() || !Resource.class.isAssignableFrom(propertyType)) {
+ throw new IllegalArgumentException("Property " + rootIface.getName() + "." + propertyName +
+ " has an illegal type " + propertyType.getName());
+ }
+ }
+ if (!propertyTypes.containsKey(propertyName)) {
+ propertyTypes.put(propertyName, propertyType);
+ }
+ }
+
+ // Scan superinterfaces
+ for (Class> superIface : iface.getInterfaces()) {
+ scanIface(superIface);
+ }
+ }
+
+ private void throwInvalidMethod(Method method) {
+ throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." +
+ method.getName() + " is not likely to be either getter or setter");
+ }
+
+ private void scanGetter(Method method) {
+ String propertyName = extractPropertyName(method.getName().substring(3));
+ if (propertyName == null || method.getReturnType().equals(void.class) ||
+ method.getParameterTypes().length > 0) {
+ throwInvalidMethod(method);
+ }
+ if (getters.put(propertyName, method.getReturnType()) != null) {
+ throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." +
+ method.getName() + " is a duplicate getter for property " + propertyName);
+ }
+ methods.put(method, new BuildTimeResourceGetter(getPropertyIndex(propertyName)));
+ }
+
+ private void scanBooleanGetter(Method method) {
+ String propertyName = extractPropertyName(method.getName().substring(2));
+ if (propertyName == null || !method.getReturnType().equals(boolean.class) ||
+ method.getParameterTypes().length > 0) {
+ throwInvalidMethod(method);
+ }
+ if (getters.put(propertyName, method.getReturnType()) != null) {
+ throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." +
+ method.getName() + " is a duplicate getter for property " + propertyName);
+ }
+ methods.put(method, new BuildTimeResourceGetter(getPropertyIndex(propertyName)));
+ }
+
+ private void scanSetter(Method method) {
+ String propertyName = extractPropertyName(method.getName().substring(3));
+ if (propertyName == null || !method.getReturnType().equals(void.class) ||
+ method.getParameterTypes().length != 1) {
+ throwInvalidMethod(method);
+ }
+ if (setters.put(propertyName, method.getParameterTypes()[0]) != null) {
+ throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." +
+ method.getName() + " is a duplicate setter for property " + propertyName);
+ }
+ methods.put(method, new BuildTimeResourceSetter(getPropertyIndex(propertyName)));
+ }
+
+ private String extractPropertyName(String propertyName) {
+ if (propertyName.isEmpty()) {
+ return null;
+ }
+ char c = propertyName.charAt(0);
+ if (c != Character.toUpperCase(c)) {
+ return null;
+ }
+ if (propertyName.length() == 1) {
+ return propertyName.toLowerCase();
+ }
+ c = propertyName.charAt(1);
+ if (c == Character.toUpperCase(c)) {
+ return propertyName;
+ } else {
+ return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+ }
+ }
+
+ private int getPropertyIndex(String propertyName) {
+ Integer index = propertyIndexes.get(propertyName);
+ if (index == null) {
+ index = propertyIndexes.size();
+ propertyIndexes.put(propertyName, index);
+ }
+ return index;
+ }
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java
new file mode 100644
index 000000000..4283bda22
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceProxyFactory {
+ private Map methods = new HashMap<>();
+ private Object[] initialData;
+
+ public BuildTimeResourceProxyFactory(Map methods, Object[] initialData) {
+ this.methods = methods;
+ this.initialData = initialData;
+ }
+
+ BuildTimeResourceProxy create() {
+ return new BuildTimeResourceProxy(methods, initialData);
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java
new file mode 100644
index 000000000..e8caaa40d
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceSetter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceSetter implements BuildTimeResourceMethod {
+ private int index;
+
+ public BuildTimeResourceSetter(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable {
+ proxy.data[index] = args[0];
+ return null;
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java
new file mode 100644
index 000000000..dead2901c
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.codegen.SourceWriter;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod {
+ private String[] propertyNames;
+
+ public BuildTimeResourceWriterMethod(String[] propertyNames) {
+ this.propertyNames = propertyNames;
+ }
+
+ @Override
+ public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable {
+ SourceWriter writer = (SourceWriter)args[0];
+ writer.append('{');
+ for (int i = 0; i < propertyNames.length; ++i) {
+ if (i > 0) {
+ writer.append(',').ws();
+ }
+ ResourceWriterHelper.writeString(writer, propertyNames[i]);
+ writer.ws().append(':').ws();
+ ResourceWriterHelper.write(writer, proxy.data[i]);
+ }
+ writer.append('}').tokenBoundary();
+ return null;
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java
new file mode 100644
index 000000000..947202715
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.lang.reflect.Proxy;
+import java.util.Properties;
+import org.teavm.common.ServiceRepository;
+import org.teavm.model.ListableClassReaderSource;
+import org.teavm.platform.metadata.MetadataGeneratorContext;
+import org.teavm.platform.metadata.Resource;
+import org.teavm.platform.metadata.ResourceArray;
+import org.teavm.platform.metadata.ResourceMap;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
+ private ListableClassReaderSource classSource;
+ private ClassLoader classLoader;
+ private Properties properties;
+ private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder();
+ private ServiceRepository services;
+
+ public DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader,
+ Properties properties, ServiceRepository services) {
+ this.classSource = classSource;
+ this.classLoader = classLoader;
+ this.properties = properties;
+ this.services = services;
+ }
+
+ @Override
+ public ListableClassReaderSource getClassSource() {
+ return classSource;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ @Override
+ public Properties getProperties() {
+ return new Properties(properties);
+ }
+
+ @Override
+ public T createResource(Class resourceType) {
+ return resourceType.cast(Proxy.newProxyInstance(classLoader,
+ new Class>[] { resourceType, ResourceWriter.class },
+ proxyBuilder.buildProxy(resourceType)));
+ }
+
+ @Override
+ public ResourceArray createResourceArray() {
+ return new BuildTimeResourceArray<>();
+ }
+
+ @Override
+ public ResourceMap createResourceMap() {
+ return new BuildTimeResourceMap<>();
+ }
+
+ @Override
+ public T getService(Class type) {
+ return services.getService(type);
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java
new file mode 100644
index 000000000..da0dd8740
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.javascript.ni.Generator;
+import org.teavm.javascript.ni.GeneratorContext;
+import org.teavm.model.*;
+import org.teavm.platform.metadata.MetadataGenerator;
+import org.teavm.platform.metadata.MetadataProvider;
+import org.teavm.platform.metadata.Resource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class MetadataProviderNativeGenerator implements Generator {
+ @Override
+ public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
+ // Validate method
+ ClassReader cls = context.getClassSource().get(methodRef.getClassName());
+ MethodReader method = cls.getMethod(methodRef.getDescriptor());
+ AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
+ if (providerAnnot == null) {
+ return;
+ }
+ if (!method.hasModifier(ElementModifier.NATIVE)) {
+ throw new IllegalStateException("Method " + method.getReference() + " was marked with " +
+ MetadataProvider.class.getName() + " but it is not native");
+ }
+
+ // Find and instantiate metadata generator
+ ValueType generatorType = providerAnnot.getValue("value").getJavaClass();
+ String generatorClassName = ((ValueType.Object)generatorType).getClassName();
+ Class> generatorClass;
+ try {
+ generatorClass = Class.forName(generatorClassName, true, context.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Can't find metadata generator class: " + generatorClassName, e);
+ }
+ Constructor> cons;
+ try {
+ cons = generatorClass.getConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Metadata generator " + generatorClassName + " does not have a public " +
+ "no-arg constructor", e);
+ }
+ MetadataGenerator generator;
+ try {
+ generator = (MetadataGenerator)cons.newInstance();
+ } catch (IllegalAccessException | InstantiationException e) {
+ throw new RuntimeException("Error instantiating metadata generator " + generatorClassName, e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("Error instantiating metadata generator " + generatorClassName,
+ e.getTargetException());
+ }
+ DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
+ context.getClassLoader(), context.getProperties(), context);
+
+ // Generate resource loader
+ Resource resource = generator.generateMetadata(metadataContext, methodRef);
+ writer.append("if (!window.hasOwnProperty(\"").appendMethodBody(methodRef).append("$$resource\")) {")
+ .indent().softNewLine();
+ writer.appendMethodBody(methodRef).append("$$resource = ");
+ ResourceWriterHelper.write(writer, resource);
+ writer.append(';').softNewLine();
+ writer.outdent().append('}').softNewLine();
+ writer.append("return ").appendMethodBody(methodRef).append("$$resource;").softNewLine();
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java
new file mode 100644
index 000000000..f8d25aadf
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.javascript.ni.GeneratedBy;
+import org.teavm.model.*;
+import org.teavm.platform.metadata.MetadataProvider;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class MetadataProviderTransformer implements ClassHolderTransformer {
+ @Override
+ public void transformClass(ClassHolder cls, ClassReaderSource innerSource) {
+ for (MethodHolder method : cls.getMethods()) {
+ AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
+ if (providerAnnot == null) {
+ continue;
+ }
+ AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
+ genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
+ MetadataProviderNativeGenerator.class.getName())));
+ method.getAnnotations().add(genAnnot);
+ }
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java
new file mode 100644
index 000000000..ad16bfc25
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.vm.spi.TeaVMHost;
+import org.teavm.vm.spi.TeaVMPlugin;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class PlatformPlugin implements TeaVMPlugin {
+ @Override
+ public void install(TeaVMHost host) {
+ host.add(new MetadataProviderTransformer());
+ host.add(new ResourceTransformer());
+ host.add(new ResourceAccessorTransformer(host));
+ host.add(new ResourceAccessorDependencyListener());
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java
new file mode 100644
index 000000000..5002ec399
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessor.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.platform.metadata.Resource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+final class ResourceAccessor {
+ public static native Object get(Object obj, String propertyName);
+
+ public static native void put(Object obj, String propertyName, Object elem);
+
+ public static native Resource get(Object obj, int index);
+
+ public static native void add(Object obj, Resource elem);
+
+ public static native boolean has(Object obj, String key);
+
+ public static native int size(Object obj);
+
+ public static native int castToInt(Object obj);
+
+ public static native short castToShort(Object obj);
+
+ public static native byte castToByte(Object obj);
+
+ public static native boolean castToBoolean(Object obj);
+
+ public static native float castToFloat(Object obj);
+
+ public static native double castToDouble(Object obj);
+
+ public static native String castToString(Object obj);
+
+ public static native Object castFromInt(int value);
+
+ public static native Object castFromShort(short value);
+
+ public static native Object castFromByte(byte value);
+
+ public static native Object castFromBoolean(boolean value);
+
+ public static native Object castFromFloat(float value);
+
+ public static native Object castFromDouble(double value);
+
+ public static native Object castFromString(String value);
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java
new file mode 100644
index 000000000..72c572674
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.dependency.DependencyAgent;
+import org.teavm.dependency.DependencyListener;
+import org.teavm.dependency.FieldDependency;
+import org.teavm.dependency.MethodDependency;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class ResourceAccessorDependencyListener implements DependencyListener {
+ @Override
+ public void started(DependencyAgent agent) {
+ }
+
+ @Override
+ public void classAchieved(DependencyAgent agent, String className) {
+ }
+
+ @Override
+ public void methodAchieved(DependencyAgent agent, MethodDependency method) {
+ switch (method.getReference().getName()) {
+ case "castToString":
+ method.getResult().propagate("java.lang.String");
+ break;
+ }
+ }
+
+ @Override
+ public void fieldAchieved(DependencyAgent agent, FieldDependency field) {
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
new file mode 100644
index 000000000..1eaa46a52
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.io.IOException;
+import org.teavm.javascript.ast.ConstantExpr;
+import org.teavm.javascript.ast.Expr;
+import org.teavm.javascript.ni.Injector;
+import org.teavm.javascript.ni.InjectorContext;
+import org.teavm.model.MethodReference;
+import org.teavm.model.ValueType;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class ResourceAccessorGenerator implements Injector {
+ @Override
+ public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
+ switch (methodRef.getName()) {
+ case "get":
+ if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append('[');
+ context.writeExpr(context.getArgument(1));
+ context.getWriter().append(']');
+ } else {
+ context.writeExpr(context.getArgument(0));
+ writePropertyAccessor(context, context.getArgument(1));
+ }
+ break;
+ case "put":
+ context.getWriter().append('(');
+ if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append('[');
+ context.writeExpr(context.getArgument(1));
+ } else {
+ context.writeExpr(context.getArgument(0));
+ writePropertyAccessor(context, context.getArgument(1));
+ }
+ context.getWriter().ws().append('=').ws();
+ context.writeExpr(context.getArgument(2));
+ context.getWriter().append(')');
+ break;
+ case "add":
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append(".push(");
+ context.writeExpr(context.getArgument(1));
+ context.getWriter().append(')');
+ break;
+ case "has":
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append(".hasOwnProperty(");
+ writeStringExpr(context, context.getArgument(1));
+ context.getWriter().append(')');
+ break;
+ case "size":
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append(".length");
+ break;
+ case "castToInt":
+ case "castToShort":
+ case "castToByte":
+ case "castToBoolean":
+ case "castToFloat":
+ case "castToDouble":
+ case "castFromInt":
+ case "castFromShort":
+ case "castFromByte":
+ case "castFromBoolean":
+ case "castFromFloat":
+ case "castFromDouble":
+ context.writeExpr(context.getArgument(0));
+ break;
+ case "castToString":
+ context.getWriter().append('(');
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws();
+ context.getWriter().append("$rt_str(");
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append(")").ws().append(':').ws().append("null)");
+ break;
+ case "castFromString":
+ context.getWriter().append('(');
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws();
+ context.getWriter().append("$rt_ustr(");
+ context.writeExpr(context.getArgument(0));
+ context.getWriter().append(")").ws().append(':').ws().append("null)");
+ break;
+ }
+ }
+
+ private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException {
+ if (property instanceof ConstantExpr) {
+ String str = (String)((ConstantExpr)property).getValue();
+ if (str.isEmpty()) {
+ context.getWriter().append("[\"\"]");
+ return;
+ }
+ if (isValidIndentifier(str)) {
+ context.getWriter().append(".").append(str);
+ return;
+ }
+ }
+ context.getWriter().append("[$rt_ustr(");
+ context.writeExpr(property);
+ context.getWriter().append(")]");
+ }
+
+ private void writeStringExpr(InjectorContext context, Expr expr) throws IOException {
+ if (expr instanceof ConstantExpr) {
+ String str = (String)((ConstantExpr)expr).getValue();
+ context.getWriter().append('"');
+ context.writeEscaped(str);
+ context.getWriter().append('"');
+ return;
+ }
+ context.getWriter().append("$rt_ustr(");
+ context.writeExpr(expr);
+ context.getWriter().append(")");
+ }
+
+ private boolean isValidIndentifier(String str) {
+ if (!Character.isJavaIdentifierStart(str.charAt(0))) {
+ return false;
+ }
+ for (int i = 1; i < str.length(); ++i) {
+ if (!Character.isJavaIdentifierPart(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java
new file mode 100644
index 000000000..6a3819013
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorTransformer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.model.ClassHolder;
+import org.teavm.model.ClassHolderTransformer;
+import org.teavm.model.ClassReaderSource;
+import org.teavm.model.MethodHolder;
+import org.teavm.vm.spi.TeaVMHost;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class ResourceAccessorTransformer implements ClassHolderTransformer {
+ private TeaVMHost vm;
+
+ public ResourceAccessorTransformer(TeaVMHost vm) {
+ this.vm = vm;
+ }
+
+ @Override
+ public void transformClass(ClassHolder cls, ClassReaderSource innerSource) {
+ if (cls.getName().equals(ResourceAccessor.class.getName())) {
+ ResourceAccessorGenerator generator = new ResourceAccessorGenerator();
+ for (MethodHolder method : cls.getMethods()) {
+ vm.add(method.getReference(), generator);
+ }
+ }
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java
new file mode 100644
index 000000000..56e51343b
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java
@@ -0,0 +1,285 @@
+package org.teavm.platform.plugin;
+
+import java.util.*;
+import org.teavm.model.*;
+import org.teavm.model.instructions.*;
+import org.teavm.platform.metadata.Resource;
+import org.teavm.platform.metadata.ResourceArray;
+import org.teavm.platform.metadata.ResourceMap;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class ResourceProgramTransformer {
+ private ClassReaderSource innerSource;
+ private Program program;
+
+ public ResourceProgramTransformer(ClassReaderSource innerSource, Program program) {
+ this.innerSource = innerSource;
+ this.program = program;
+ }
+
+ public void transformProgram() {
+ for (int i = 0; i < program.basicBlockCount(); ++i) {
+ transformBasicBlock(program.basicBlockAt(i));
+ }
+ }
+
+ private void transformBasicBlock(BasicBlock block) {
+ List instructions = block.getInstructions();
+ for (int i = 0; i < instructions.size(); ++i) {
+ Instruction insn = instructions.get(i);
+ if (insn instanceof InvokeInstruction) {
+ InvokeInstruction invoke = (InvokeInstruction)insn;
+ List replacement = transformInvoke(invoke);
+ if (replacement != null) {
+ instructions.set(i, new EmptyInstruction());
+ instructions.addAll(i, replacement);
+ i += replacement.size();
+ }
+ }
+ }
+ }
+
+ private List transformInvoke(InvokeInstruction insn) {
+ if (insn.getType() != InvocationType.VIRTUAL) {
+ return null;
+ }
+ MethodReference method = insn.getMethod();
+ if (method.getClassName().equals(ResourceArray.class.getName()) ||
+ method.getClassName().equals(ResourceMap.class.getName())) {
+ InvokeInstruction accessInsn = new InvokeInstruction();
+ accessInsn.setType(InvocationType.SPECIAL);
+ ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2];
+ types[0] = ValueType.object("java.lang.Object");
+ System.arraycopy(method.getDescriptor().getSignature(), 0, types, 1,
+ method.getDescriptor().parameterCount() + 1);
+ accessInsn.setMethod(new MethodReference(ResourceAccessor.class.getName(), method.getName(), types));
+ accessInsn.getArguments().add(insn.getInstance());
+ accessInsn.getArguments().addAll(insn.getArguments());
+ accessInsn.setReceiver(insn.getReceiver());
+ return Arrays.asList(accessInsn);
+ }
+ ClassReader iface = innerSource.get(method.getClassName());
+ if (!isSubclass(iface, Resource.class.getName())) {
+ return null;
+ }
+ if (method.getName().startsWith("get")) {
+ if (method.getName().length() > 3) {
+ return transformGetterInvocation(insn, getPropertyName(method.getName().substring(3)));
+ }
+ } else if (method.getName().startsWith("is")) {
+ if (method.getName().length() > 2) {
+ return transformGetterInvocation(insn, getPropertyName(method.getName().substring(2)));
+ }
+ } else if (method.getName().startsWith("set")) {
+ if (method.getName().length() > 3) {
+ return transformSetterInvocation(insn, getPropertyName(method.getName().substring(3)));
+ }
+ }
+ return null;
+ }
+
+ private boolean isSubclass(ClassReader cls, String superClass) {
+ if (cls.getName().equals(superClass)) {
+ return true;
+ }
+ ClassReader parent = cls.getParent() != null ? innerSource.get(cls.getParent()) : null;
+ if (parent != null && isSubclass(parent, superClass)) {
+ return true;
+ }
+ for (String ifaceName : cls.getInterfaces()) {
+ ClassReader iface = innerSource.get(ifaceName);
+ if (iface != null) {
+ return isSubclass(iface, superClass);
+ }
+ }
+ return false;
+ }
+
+ private List transformGetterInvocation(InvokeInstruction insn, String property) {
+ if (insn.getReceiver() == null) {
+ return Collections.emptyList();
+ }
+ ValueType type = insn.getMethod().getDescriptor().getResultType();
+ List instructions = new ArrayList<>();
+ if (type instanceof ValueType.Primitive) {
+ switch (((ValueType.Primitive)type).getKind()) {
+ case BOOLEAN:
+ getAndCastProperty(insn, property, instructions, boolean.class);
+ return instructions;
+ case BYTE:
+ getAndCastProperty(insn, property, instructions, byte.class);
+ return instructions;
+ case SHORT:
+ getAndCastProperty(insn, property, instructions, short.class);
+ return instructions;
+ case INTEGER:
+ getAndCastProperty(insn, property, instructions, int.class);
+ return instructions;
+ case FLOAT:
+ getAndCastProperty(insn, property, instructions, float.class);
+ return instructions;
+ case DOUBLE:
+ getAndCastProperty(insn, property, instructions, double.class);
+ return instructions;
+ case CHARACTER:
+ case LONG:
+ break;
+ }
+ } else if (type instanceof ValueType.Object) {
+ switch (((ValueType.Object)type).getClassName()) {
+ case "java.lang.String": {
+ Variable resultVar = insn.getProgram().createVariable();
+ getProperty(insn, property, instructions, resultVar);
+ InvokeInstruction castInvoke = new InvokeInstruction();
+ castInvoke.setType(InvocationType.SPECIAL);
+ castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castToString",
+ Object.class, String.class));
+ castInvoke.getArguments().add(resultVar);
+ castInvoke.setReceiver(insn.getReceiver());
+ instructions.add(castInvoke);
+ return instructions;
+ }
+ default: {
+ Variable resultVar = insn.getProgram().createVariable();
+ getProperty(insn, property, instructions, resultVar);
+ CastInstruction castInsn = new CastInstruction();
+ castInsn.setReceiver(insn.getReceiver());
+ castInsn.setTargetType(type);
+ castInsn.setValue(resultVar);
+ instructions.add(castInsn);
+ return instructions;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void getProperty(InvokeInstruction insn, String property, List instructions,
+ Variable resultVar) {
+ Variable nameVar = program.createVariable();
+ StringConstantInstruction nameInsn = new StringConstantInstruction();
+ nameInsn.setConstant(property);
+ nameInsn.setReceiver(nameVar);
+ instructions.add(nameInsn);
+ InvokeInstruction accessorInvoke = new InvokeInstruction();
+ accessorInvoke.setType(InvocationType.SPECIAL);
+ accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "get",
+ Object.class, String.class, Object.class));
+ accessorInvoke.getArguments().add(insn.getInstance());
+ accessorInvoke.getArguments().add(nameVar);
+ accessorInvoke.setReceiver(resultVar);
+ instructions.add(accessorInvoke);
+ }
+
+ private void getAndCastProperty(InvokeInstruction insn, String property, List instructions,
+ Class> primitive) {
+ Variable resultVar = program.createVariable();
+ getProperty(insn, property, instructions, resultVar);
+ InvokeInstruction castInvoke = new InvokeInstruction();
+ castInvoke.setType(InvocationType.SPECIAL);
+ String primitiveCapitalized = primitive.getName();
+ primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) +
+ primitiveCapitalized.substring(1);
+ castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castTo" + primitiveCapitalized,
+ Object.class, primitive));
+ castInvoke.getArguments().add(resultVar);
+ castInvoke.setReceiver(insn.getReceiver());
+ instructions.add(castInvoke);
+ }
+
+ private List transformSetterInvocation(InvokeInstruction insn, String property) {
+ ValueType type = insn.getMethod().getDescriptor().parameterType(0);
+ List instructions = new ArrayList<>();
+ if (type instanceof ValueType.Primitive) {
+ switch (((ValueType.Primitive)type).getKind()) {
+ case BOOLEAN:
+ castAndSetProperty(insn, property, instructions, boolean.class);
+ return instructions;
+ case BYTE:
+ castAndSetProperty(insn, property, instructions, byte.class);
+ return instructions;
+ case SHORT:
+ castAndSetProperty(insn, property, instructions, short.class);
+ return instructions;
+ case INTEGER:
+ castAndSetProperty(insn, property, instructions, int.class);
+ return instructions;
+ case FLOAT:
+ castAndSetProperty(insn, property, instructions, float.class);
+ return instructions;
+ case DOUBLE:
+ castAndSetProperty(insn, property, instructions, double.class);
+ return instructions;
+ case CHARACTER:
+ case LONG:
+ break;
+ }
+ } else if (type instanceof ValueType.Object) {
+ switch (((ValueType.Object)type).getClassName()) {
+ case "java.lang.String": {
+ Variable castVar = insn.getProgram().createVariable();
+ InvokeInstruction castInvoke = new InvokeInstruction();
+ castInvoke.setType(InvocationType.SPECIAL);
+ castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFromString",
+ String.class, Object.class));
+ castInvoke.getArguments().add(insn.getArguments().get(0));
+ castInvoke.setReceiver(castVar);
+ instructions.add(castInvoke);
+ setProperty(insn, property, instructions, castVar);
+ return instructions;
+ }
+ default: {
+ setProperty(insn, property, instructions, insn.getArguments().get(0));
+ return instructions;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void setProperty(InvokeInstruction insn, String property, List instructions,
+ Variable valueVar) {
+ Variable nameVar = program.createVariable();
+ StringConstantInstruction nameInsn = new StringConstantInstruction();
+ nameInsn.setConstant(property);
+ nameInsn.setReceiver(nameVar);
+ instructions.add(nameInsn);
+ InvokeInstruction accessorInvoke = new InvokeInstruction();
+ accessorInvoke.setType(InvocationType.SPECIAL);
+ accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "put",
+ Object.class, String.class, Object.class, void.class));
+ accessorInvoke.getArguments().add(insn.getInstance());
+ accessorInvoke.getArguments().add(nameVar);
+ accessorInvoke.getArguments().add(valueVar);
+ instructions.add(accessorInvoke);
+ }
+
+ private void castAndSetProperty(InvokeInstruction insn, String property, List instructions,
+ Class> primitive) {
+ Variable castVar = program.createVariable();
+ InvokeInstruction castInvoke = new InvokeInstruction();
+ castInvoke.setType(InvocationType.SPECIAL);
+ String primitiveCapitalized = primitive.getName();
+ primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) +
+ primitiveCapitalized.substring(1);
+ castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFrom" + primitiveCapitalized,
+ primitive, Object.class));
+ castInvoke.getArguments().add(insn.getArguments().get(0));
+ castInvoke.setReceiver(castVar);
+ instructions.add(castInvoke);
+ setProperty(insn, property, instructions, castVar);
+ }
+
+ private String getPropertyName(String name) {
+ if (name.length() == 1) {
+ return name.toLowerCase();
+ }
+ if (Character.isUpperCase(name.charAt(1))) {
+ return name;
+ }
+ return Character.toLowerCase(name.charAt(0)) + name.substring(1);
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java
new file mode 100644
index 000000000..f0b8854d2
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceTransformer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import org.teavm.model.*;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+class ResourceTransformer implements ClassHolderTransformer {
+ @Override
+ public void transformClass(ClassHolder cls, ClassReaderSource innerSource) {
+ for (MethodHolder method : cls.getMethods()) {
+ Program program = method.getProgram();
+ if (program != null) {
+ new ResourceProgramTransformer(innerSource, program).transformProgram();
+ }
+ }
+ }
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java
new file mode 100644
index 000000000..b457a5d70
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface ResourceWriter {
+ void write(SourceWriter writer) throws IOException;
+}
diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java
new file mode 100644
index 000000000..b263a3f24
--- /dev/null
+++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.platform.plugin;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+final class ResourceWriterHelper {
+ public static void write(SourceWriter writer, Object resource) throws IOException {
+ if (resource == null) {
+ writer.append("null");
+ } else {
+ if (resource instanceof ResourceWriter) {
+ ((ResourceWriter)resource).write(writer);
+ } else if (resource instanceof Number) {
+ writer.append(resource);
+ } else if (resource instanceof Boolean) {
+ writer.append(resource == Boolean.TRUE ? "true" : "false");
+ } else if (resource instanceof String) {
+ writeString(writer, (String)resource);
+ } else {
+ throw new RuntimeException("Error compiling resources. Value of illegal type found: " +
+ resource.getClass());
+ }
+ }
+ }
+
+ public static void writeString(SourceWriter writer, String s) throws IOException {
+ writer.append('"');
+ for (int i = 0; i < s.length(); ++i) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '\0':
+ writer.append("\\0");
+ break;
+ case '\n':
+ writer.append("\\n");
+ break;
+ case '\r':
+ writer.append("\\r");
+ break;
+ case '\t':
+ writer.append("\\t");
+ break;
+ default:
+ if (c < 32) {
+ writer.append("\\u00").append(Character.forDigit(c / 16, 16))
+ .append(Character.forDigit(c % 16, 16));
+ } else {
+ writer.append(c);
+ }
+ break;
+ }
+ }
+ writer.append('"');
+ }
+}
diff --git a/teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin b/teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin
new file mode 100644
index 000000000..03f8f7273
--- /dev/null
+++ b/teavm-platform/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin
@@ -0,0 +1 @@
+org.teavm.platform.plugin.PlatformPlugin
\ No newline at end of file