classes = new ConcurrentHashMap<>();
+
+ @Override
+ public ClassHolder getClassHolder(String name) {
+ return classes.get(name);
+ }
+
+ public void putClassHolder(ClassHolder classHolder) {
+ classes.put(classHolder.getName(), classHolder);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/package-info.java b/teavm-core/src/main/java/org/teavm/model/package-info.java
index 07eb567c9..b002ea412 100644
--- a/teavm-core/src/main/java/org/teavm/model/package-info.java
+++ b/teavm-core/src/main/java/org/teavm/model/package-info.java
@@ -4,7 +4,7 @@
* it allows to disassemble method bodies into three-address code that is very
* close to JVM bytecode (see {@link org.teavm.instructions}.
*
- * The entry point is some implementation of {@link ClassHolderSource} class.
+ *
The entry point is some implementation of {@link MutableClassHolderSource} class.
*
*/
package org.teavm.model;
\ No newline at end of file
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java
new file mode 100644
index 000000000..b5d892689
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java
@@ -0,0 +1,28 @@
+package org.teavm.model.resource;
+
+import org.teavm.model.ClassHolder;
+import org.teavm.model.ClassHolderSource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class ClasspathClassHolderSource implements ClassHolderSource {
+ private MapperClassHolderSource innerClassSource;
+
+ public ClasspathClassHolderSource(ClassLoader classLoader) {
+ ClasspathResourceReader reader = new ClasspathResourceReader();
+ ResourceClassHolderMapper rawMapper = new ResourceClassHolderMapper(reader);
+ ClasspathResourceMapper classPathMapper = new ClasspathResourceMapper(classLoader, rawMapper);
+ innerClassSource = new MapperClassHolderSource(classPathMapper);
+ }
+
+ public ClasspathClassHolderSource() {
+ this(ClasspathClassHolderSource.class.getClassLoader());
+ }
+
+ @Override
+ public ClassHolder getClassHolder(String name) {
+ return innerClassSource.getClassHolder(name);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java
new file mode 100644
index 000000000..6efe96e84
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java
@@ -0,0 +1,83 @@
+package org.teavm.model.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+import org.teavm.codegen.Mapper;
+import org.teavm.model.ClassHolder;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class ClasspathResourceMapper implements Mapper {
+ private static String PACKAGE_PREFIX = "packagePrefix.";
+ private static String CLASS_PREFIX = "classPrefix.";
+ private Mapper innerMapper;
+ private List transformations = new ArrayList<>();
+
+ private static class Transformation {
+ String packageName;
+ String packagePrefix = "";
+ String classPrefix = "";
+ }
+
+ public ClasspathResourceMapper(ClassLoader classLoader, Mapper innerMapper) {
+ this.innerMapper = innerMapper;
+ try {
+ Enumeration resources = classLoader.getResources("META-INF/teavm.properties");
+ Map transformationMap = new HashMap<>();
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ Properties properties = new Properties();
+ try (InputStream input = resource.openStream()) {
+ properties.load(input);
+ }
+ loadProperties(properties, transformationMap);
+ }
+ transformations.addAll(transformationMap.values());
+ } catch (IOException e) {
+ throw new RuntimeException("Error reading resources", e);
+ }
+ }
+
+ private void loadProperties(Properties properties, Map cache) {
+ for (String propertyName : properties.stringPropertyNames()) {
+ if (propertyName.startsWith(PACKAGE_PREFIX)) {
+ String packageName = propertyName.substring(PACKAGE_PREFIX.length());
+ Transformation transformation = getTransformation(cache, packageName);
+ transformation.packagePrefix = properties.getProperty(propertyName) + ".";
+ } else if (propertyName.startsWith(CLASS_PREFIX)) {
+ String packageName = propertyName.substring(CLASS_PREFIX.length());
+ Transformation transformation = getTransformation(cache, packageName);
+ transformation.classPrefix = properties.getProperty(propertyName);
+ }
+ }
+ }
+
+ private Transformation getTransformation(Map cache, String packageName) {
+ Transformation transformation = cache.get(packageName);
+ if (transformation == null) {
+ transformation = new Transformation();
+ transformation.packageName = packageName;
+ cache.put(packageName, transformation);
+ }
+ return transformation;
+ }
+
+ @Override
+ public ClassHolder map(String name) {
+ for (Transformation transformation : transformations) {
+ if (name.startsWith(transformation.packageName)) {
+ int index = name.lastIndexOf('.');
+ String className = name.substring(index + 1);
+ String packageName = name.substring(0, index);
+ ClassHolder classHolder = innerMapper.map(transformation.packagePrefix + "." + packageName +
+ "." + transformation.classPrefix + className);
+ return classHolder;
+ }
+ }
+ return innerMapper.map(name);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java
new file mode 100644
index 000000000..7b2a84bb1
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java
@@ -0,0 +1,30 @@
+package org.teavm.model.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class ClasspathResourceReader implements ResourceReader {
+ private ClassLoader classLoader;
+
+ public ClasspathResourceReader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public ClasspathResourceReader() {
+ this(ClasspathResourceReader.class.getClassLoader());
+ }
+
+ @Override
+ public boolean hasResource(String name) {
+ return classLoader.getResource(name) != null;
+ }
+
+ @Override
+ public InputStream openResource(String name) throws IOException {
+ return classLoader.getResourceAsStream(name);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java b/teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java
new file mode 100644
index 000000000..781df8bbb
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java
@@ -0,0 +1,23 @@
+package org.teavm.model.resource;
+
+import org.teavm.codegen.ConcurrentCachedMapper;
+import org.teavm.codegen.Mapper;
+import org.teavm.model.ClassHolder;
+import org.teavm.model.ClassHolderSource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class MapperClassHolderSource implements ClassHolderSource {
+ private Mapper mapper;
+
+ public MapperClassHolderSource(Mapper mapper) {
+ this.mapper = new ConcurrentCachedMapper<>(mapper);
+ }
+
+ @Override
+ public ClassHolder getClassHolder(String name) {
+ return mapper.map(name);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java b/teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java
new file mode 100644
index 000000000..e2bc77066
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java
@@ -0,0 +1,37 @@
+package org.teavm.model.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.teavm.codegen.Mapper;
+import org.teavm.model.ClassHolder;
+import org.teavm.parsing.Parser;
+
+/**
+ *
+ * @author konsoletyper
+ */
+public class ResourceClassHolderMapper implements Mapper {
+ private ResourceReader resourceReader;
+
+ public ResourceClassHolderMapper(ResourceReader resourceReader) {
+ this.resourceReader = resourceReader;
+ }
+
+ @Override
+ public ClassHolder map(String name) {
+ ClassNode clsNode = new ClassNode();
+ String resourceName = name.replace('.', '/') + ".class";
+ if (!resourceReader.hasResource(resourceName)) {
+ return null;
+ }
+ try (InputStream input = resourceReader.openResource(resourceName)) {
+ ClassReader reader = new ClassReader(input);
+ reader.accept(clsNode, 0);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return Parser.parseClass(clsNode);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java b/teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java
new file mode 100644
index 000000000..ad04a0720
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java
@@ -0,0 +1,33 @@
+package org.teavm.model.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.teavm.codegen.Mapper;
+import org.teavm.model.ClassHolder;
+import org.teavm.parsing.Parser;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class ResourceParser implements Mapper {
+ private ResourceReader resourceReader;
+
+ public ResourceParser(ResourceReader resourceReader) {
+ this.resourceReader = resourceReader;
+ }
+
+ @Override
+ public ClassHolder map(String name) {
+ ClassNode clsNode = new ClassNode();
+ try (InputStream input = resourceReader.openResource(name.replace('.', '/') + ".class")) {
+ ClassReader reader = new ClassReader(input);
+ reader.accept(clsNode, 0);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return Parser.parseClass(clsNode);
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java b/teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java
new file mode 100644
index 000000000..d342dae29
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java
@@ -0,0 +1,14 @@
+package org.teavm.model.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *
+ * @author konsoletyper
+ */
+public interface ResourceReader {
+ boolean hasResource(String name);
+
+ InputStream openResource(String name) throws IOException;
+}
diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js
new file mode 100644
index 000000000..ad13d3c9a
--- /dev/null
+++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js
@@ -0,0 +1,316 @@
+$rt_lastObjectId = 0;
+$rt_nextId = function() {
+ return $rt_lastObjectId++;
+}
+$rt_compare = function(a, b) {
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+$rt_isInstance = function(obj, cls) {
+ return $rt_isAssignable(obj.$class, cls);
+}
+$rt_isAssignable = function(from, to) {
+ if (from === to) {
+ return true;
+ }
+ var supertypes = from.$meta.supertypes;
+ for (var i = 0; i < supertypes.length; i = (i + 1) | 0) {
+ if ($rt_isAssignable(supertypes[i], to)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+$rt = {
+ createBooleanArray : function(cls, sz) {
+ var arr = $rt.createArray(cls, sz);
+ for (var i = 0; i < sz; i = (i + 1) | 0) {
+ arr[i] = false;
+ }
+ return arr;
+ },
+ createNumericArray : function(cls, sz) {
+ var arr = $rt.createArray(cls, sz);
+ for (var i = 0; i < sz; i = (i + 1) | 0) {
+ arr[i] = 0;
+ }
+ return arr;
+ },
+ createLongArray : function(sz) {
+ var arr = $rt.createArray($rt.longcls(), sz);
+ for (var i = 0; i < sz; i = (i + 1) | 0) {
+ arr[i] = Long.ZERO;
+ }
+ return arr;
+ },
+ createArray : function(cls, sz) {
+ var arr = new Array(sz);
+ arr.$class = $rt.arraycls(cls);
+ arr.$id = $rt.lastObjectId++;
+ for (var i = 0; i < sz; i = (i + 1) | 0) {
+ arr[i] = null;
+ }
+ return arr;
+ },
+ createMultiArray : function(cls, dimensions) {
+ for (var i = 1; i < dimensions.length; i = (i + 1) | 0) {
+ cls = $rt.arraycls(cls);
+ }
+ return $rt.createMultiArrayImpl(cls, dimensions, 0);
+ },
+ createMultiArrayImpl : function(cls, dimensions, offset) {
+ var result = $rt.createArray(cls, dimensions[offset]);
+ offset = (offset + 1) | 0;
+ if (offset < dimensions.length) {
+ cls = cls.$meta.item;
+ for (var i = 0; i < result.length; i = (i + 1) | 0) {
+ result[i] = $rt.createMultiArrayImpl(cls, dimensions, offset);
+ }
+ }
+ return result;
+ },
+ initializeArray : function(cls, initial) {
+ var arr = initial.slice();
+ arr.$class = $rt.arraycls(cls);
+ $rt.setId(arr, $rt.lastObjectId++);
+ return arr;
+ },
+ arraycls : function(cls) {
+ if (cls.$array == undefined) {
+ cls.$array = {
+ $meta : { item : cls },
+ };
+ if ($rt.objcls) {
+ cls.$array.$meta.supertypes = [$rt.objcls()];
+ }
+ }
+ return cls.$array;
+ },
+ createcls : function() {
+ return {
+ $meta : {
+ supertypes : []
+ }
+ };
+ },
+ booleancls : function() {
+ if ($rt.booleanclsCache == null) {
+ $rt.booleanclsCache = $rt.createcls();
+ }
+ return $rt.booleanclsCache;
+ },
+ charcls : function() {
+ if ($rt.charclsCache == null) {
+ $rt.charclsCache = $rt.createcls();
+ }
+ return $rt.charclsCache;
+ },
+ bytecls : function() {
+ if ($rt.byteclsCache == null) {
+ $rt.byteclsCache = $rt.createcls();
+ }
+ return $rt.byteclsCache;
+ },
+ shortcls : function() {
+ if ($rt.shortclsCache == null) {
+ $rt.shortclsCache = $rt.createcls();
+ }
+ return $rt.shortclsCache;
+ },
+ intcls : function() {
+ if ($rt.intclsCache == null) {
+ $rt.intclsCache = $rt.createcls();
+ }
+ return $rt.intclsCache;
+ },
+ longcls : function() {
+ if ($rt.longclsCache == null) {
+ $rt.longclsCache = $rt.createcls();
+ }
+ return $rt.longclsCache;
+ },
+ floatcls : function() {
+ if ($rt.floatclsCache == null) {
+ $rt.floatclsCache = $rt.createcls();
+ }
+ return $rt.floatclsCache;
+ },
+ doublecls : function() {
+ if ($rt.doubleclsCache == null) {
+ $rt.doubleclsCache = $rt.createcls();
+ }
+ return $rt.doubleclsCache;
+ },
+ voidcls : function() {
+ if ($rt.voidclsCache == null) {
+ $rt.voidclsCache = $rt.createcls();
+ }
+ return $rt.voidclsCache;
+ },
+ equals : function(a, b) {
+ if (a === b) {
+ return true;
+ }
+ if (a === null || b === null) {
+ return false;
+ }
+ if (typeof(a) == 'object') {
+ return a.equals(b);
+ } else {
+ return false;
+ }
+ },
+ clinit : function(cls) {
+ if (cls.$clinit) {
+ var f = cls.$clinit;
+ delete cls.$clinit;
+ f();
+ }
+ return cls;
+ },
+ init : function(cls, constructor, args) {
+ var obj = new cls();
+ cls.prototype[constructor].apply(obj, args);
+ return obj;
+ },
+ assertNotNaN : function(value) {
+ if (typeof value == 'number' && isNaN(value)) {
+ throw "NaN";
+ }
+ return value;
+ }
+};
+
+Long = function(lo, hi) {
+ this.lo = lo | 0;
+ this.hi = hi | 0;
+}
+Long.ZERO = new Long(0, 0);
+Long.fromInt = function(val) {
+ return new Long(val, 0);
+}
+Long.fromNumber = function(val) {
+ return new Long(val | 0, (val / 0x100000000) | 0);
+}
+Long.toNumber = function(val) {
+ return val.lo + 0x100000000 * val.hi;
+}
+Long.add = function(a, b) {
+ var a_lolo = a.lo & 0xFFFF;
+ var a_lohi = a.lo >>> 16;
+ var a_hilo = a.hi & 0xFFFF;
+ var a_hihi = a.hi >>> 16;
+ var b_lolo = b.lo & 0xFFFF;
+ var b_lohi = b.lo >>> 16;
+ var b_hilo = b.hi & 0xFFFF;
+ var b_hihi = b.hi >>> 16;
+
+ var lolo = (a_lolo + b_lolo) | 0;
+ var lohi = (a_lohi + b_lohi + (lolo >> 16)) | 0;
+ var hilo = (a_hilo + b_hilo + (lohi >> 16)) | 0;
+ var hihi = (a_hihi + b_hihi + (hilo >> 16)) | 0;
+ return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16),
+ (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
+}
+Long.inc = function(a) {
+ var lo = (a.lo + 1) | 0;
+ var hi = a.hi;
+ if (lo === 0) {
+ hi = (hi + 1) | 0;
+ }
+ return new Long(lo, hi);
+}
+Long.dec = function(a) {
+ var lo = (a.lo - 1) | 0;
+ var hi = a.hi;
+ if (lo === -1) {
+ hi = (hi - 1) | 0;
+ }
+ return new Long(lo, hi);
+}
+Long.neg = function(a) {
+ return Long.inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF));
+}
+Long.sub = function(a, b) {
+ var a_lolo = a.lo & 0xFFFF;
+ var a_lohi = a.lo >>> 16;
+ var a_hilo = a.hi & 0xFFFF;
+ var a_hihi = a.hi >>> 16;
+ var b_lolo = b.lo & 0xFFFF;
+ var b_lohi = b.lo >>> 16;
+ var b_hilo = b.hi & 0xFFFF;
+ var b_hihi = b.hi >>> 16;
+
+ var lolo = (a_lolo - b_lolo) | 0;
+ var lohi = (a_lohi - b_lohi + (lolo >> 16)) | 0;
+ var hilo = (a_hilo - b_hilo + (lohi >> 16)) | 0;
+ var hihi = (a_hihi - b_hihi + (hilo >> 16)) | 0;
+ return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16),
+ (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
+}
+Long.compare = function(a, b) {
+ var r = a.hi - a.hi;
+ if (r != 0) {
+ return r;
+ }
+ return a.lo - b.lo;
+}
+Long.isNegative = function(a) {
+ return a.hi < 0;
+}
+Long.mul = function(a, b) {
+ var a_lolo = a.lo & 0xFFFF;
+ var a_lohi = a.lo >>> 16;
+ var a_hilo = a.hi & 0xFFFF;
+ var a_hihi = a.hi >>> 16;
+ var b_lolo = b.lo & 0xFFFF;
+ var b_lohi = b.lo >>> 16;
+ var b_hilo = b.hi & 0xFFFF;
+ var b_hihi = b.hi >>> 16;
+
+ var lolo = (a_lolo * b_lolo) | 0;
+ var lohi = (a_lohi * b_lolo + a_lolo * b_lohi + (lolo >> 16)) | 0;
+ var hilo = (a_hilo * b_lolo + a_lohi * b_lohi + a_lolo * b_hilo + (lohi >> 16)) | 0;
+ var hihi = (a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi +
+ (hilo >> 16)) | 0;
+ return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16),
+ (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
+}
+Long.div = function(a, b) {
+ var result = (a.hi * 0x100000000 + a.lo) / (b.hi * 0x100000000 + b.lo);
+ return new Long(result | 0, (result / 0x100000000) | 0);
+}
+Long.rem = function(a, b) {
+ var result = (a.hi * 0x100000000 + a.lo) % (b.hi * 0x100000000 + b.lo);
+ return new Long(result | 0, (result / 0x100000000) | 0);
+}
+Long.and = function(a, b) {
+ return new Long(a.lo & b.lo, a.hi & b.hi);
+}
+Long.or = function(a, b) {
+ return new Long(a.lo | b.lo, a.hi | b.hi);
+}
+Long.xor = function(a, b) {
+ return new Long(a.lo ^ b.lo, a.hi ^ b.hi);
+}
+Long.shl = function(a, b) {
+ if (b < 32) {
+ return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b));
+ } else {
+ return new Long(0, a.lo << (b - 32));
+ }
+}
+Long.shr = function(a, b) {
+ if (b < 32) {
+ return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b);
+ } else {
+ return new Long((a.hi >> (b - 32)), -1);
+ }
+}
+Long.shru = function(a, b) {
+ if (b < 32) {
+ return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b);
+ } else {
+ return new Long((a.hi >>> (b - 32)), 0);
+ }
+}
\ No newline at end of file