diff --git a/pom.xml b/pom.xml
index abe9225e6..43fca8ac3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,7 @@
teavm-cli
teavm-chrome-rdp
teavm-tests
+ teavm-extras-slf4j
diff --git a/teavm-extras-slf4j/.gitignore b/teavm-extras-slf4j/.gitignore
new file mode 100644
index 000000000..8bd3a0588
--- /dev/null
+++ b/teavm-extras-slf4j/.gitignore
@@ -0,0 +1,4 @@
+/target/
+/.settings/
+/.classpath
+/.project
diff --git a/teavm-extras-slf4j/pom.xml b/teavm-extras-slf4j/pom.xml
new file mode 100644
index 000000000..ddf2d0477
--- /dev/null
+++ b/teavm-extras-slf4j/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+
+
+ org.teavm
+ teavm
+ 0.3.0-SNAPSHOT
+
+ teavm-extras-slf4j
+
+ TeaVM slf4j
+ TeaVM backend for slf4j
+
+
+
+ org.teavm
+ teavm-core
+ ${project.version}
+ provided
+
+
+ org.teavm
+ teavm-jso
+ ${project.version}
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ ../checkstyle.xml
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
\ No newline at end of file
diff --git a/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/LoggerFactoryTransformer.java b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/LoggerFactoryTransformer.java
new file mode 100644
index 000000000..14bd34520
--- /dev/null
+++ b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/LoggerFactoryTransformer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 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.extras.slf4j;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.LoggerFactory;
+import org.teavm.diagnostics.Diagnostics;
+import org.teavm.model.*;
+import org.teavm.model.instructions.*;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class LoggerFactoryTransformer implements ClassHolderTransformer {
+ @Override
+ public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
+ if (!cls.getName().equals(LoggerFactory.class.getName())) {
+ return;
+ }
+ addCacheField(cls);
+ modifyClinit(cls);
+ replaceGetFactory(cls);
+ }
+
+ private void addCacheField(ClassHolder cls) {
+ FieldHolder cacheField = new FieldHolder("loggerFactoryCache");
+ cacheField.setLevel(AccessLevel.PRIVATE);
+ cacheField.getModifiers().add(ElementModifier.STATIC);
+ cacheField.setType(ValueType.object(TeaVMLoggerFactory.class.getName()));
+ cls.addField(cacheField);
+ }
+
+ private void modifyClinit(ClassHolder cls) {
+ MethodHolder clinit = cls.getMethod(new MethodDescriptor("", void.class));
+ BasicBlock clinitBlock = clinit.getProgram().basicBlockAt(0);
+ Variable factoryVar = clinit.getProgram().createVariable();
+ ConstructInstruction construct = new ConstructInstruction();
+ construct.setType(TeaVMLoggerFactory.class.getName());
+ construct.setReceiver(factoryVar);
+ clinitBlock.getInstructions().add(0, construct);
+ InvokeInstruction init = new InvokeInstruction();
+ init.setInstance(factoryVar);
+ init.setMethod(new MethodReference(TeaVMLoggerFactory.class, "", void.class));
+ init.setType(InvocationType.SPECIAL);
+ clinitBlock.getInstructions().add(1, init);
+ PutFieldInstruction put = new PutFieldInstruction();
+ put.setValue(factoryVar);
+ put.setField(new FieldReference(LoggerFactory.class.getName(), "loggerFactoryCache"));
+ clinitBlock.getInstructions().add(2, put);
+ }
+
+ private void replaceGetFactory(ClassHolder cls) {
+ MethodHolder method = cls.getMethod(new MethodDescriptor("getILoggerFactory", ILoggerFactory.class));
+ Program program = new Program();
+ BasicBlock block = program.createBasicBlock();
+ Variable cacheVar = program.createVariable();
+ GetFieldInstruction get = new GetFieldInstruction();
+ get.setField(new FieldReference(LoggerFactory.class.getName(), "loggerFactoryCache"));
+ get.setFieldType(ValueType.object(ILoggerFactory.class.getName()));
+ get.setReceiver(cacheVar);
+ block.getInstructions().add(get);
+ ExitInstruction exit = new ExitInstruction();
+ exit.setValueToReturn(cacheVar);
+ block.getInstructions().add(exit);
+ method.setProgram(program);
+ }
+}
diff --git a/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/Slf4jPlugin.java b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/Slf4jPlugin.java
new file mode 100644
index 000000000..74502095e
--- /dev/null
+++ b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/Slf4jPlugin.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 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.extras.slf4j;
+
+import org.teavm.vm.spi.TeaVMHost;
+import org.teavm.vm.spi.TeaVMPlugin;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class Slf4jPlugin implements TeaVMPlugin {
+ @Override
+ public void install(TeaVMHost host) {
+ host.add(new LoggerFactoryTransformer());
+ }
+}
\ No newline at end of file
diff --git a/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/TeaVMLogger.java b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/TeaVMLogger.java
new file mode 100644
index 000000000..2d95dc16b
--- /dev/null
+++ b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/TeaVMLogger.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2015 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.extras.slf4j;
+
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class TeaVMLogger implements Logger {
+ private String name;
+
+ public TeaVMLogger(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isTraceEnabled() {
+ return false;
+ }
+
+ @Override
+ public void trace(String msg) {
+ }
+
+ @Override
+ public void trace(String format, Object arg) {
+ }
+
+ @Override
+ public void trace(String format, Object arg1, Object arg2) {
+ }
+
+ @Override
+ public void trace(String format, Object... arguments) {
+ }
+
+ @Override
+ public void trace(String msg, Throwable t) {
+ }
+
+ @Override
+ public boolean isTraceEnabled(Marker marker) {
+ return false;
+ }
+
+ @Override
+ public void trace(Marker marker, String msg) {
+ }
+
+ @Override
+ public void trace(Marker marker, String format, Object arg) {
+ }
+
+ @Override
+ public void trace(Marker marker, String format, Object arg1, Object arg2) {
+ }
+
+ @Override
+ public void trace(Marker marker, String format, Object... argArray) {
+ }
+
+ @Override
+ public void trace(Marker marker, String msg, Throwable t) {
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ @Override
+ public void debug(String msg) {
+ }
+
+ @Override
+ public void debug(String format, Object arg) {
+ }
+
+ @Override
+ public void debug(String format, Object arg1, Object arg2) {
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+ }
+
+ @Override
+ public void debug(String msg, Throwable t) {
+ }
+
+ @Override
+ public boolean isDebugEnabled(Marker marker) {
+ return false;
+ }
+
+ @Override
+ public void debug(Marker marker, String msg) {
+ }
+
+ @Override
+ public void debug(Marker marker, String format, Object arg) {
+ }
+
+ @Override
+ public void debug(Marker marker, String format, Object arg1, Object arg2) {
+ }
+
+ @Override
+ public void debug(Marker marker, String format, Object... arguments) {
+ }
+
+ @Override
+ public void debug(Marker marker, String msg, Throwable t) {
+ }
+
+ @Override
+ public boolean isInfoEnabled() {
+ return true;
+ }
+
+ private void log(String level, String format, Object... arguments) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[').append(level).append("] ").append(name).append(": ");
+ int index = 0;
+ int argIndex = 0;
+ while (index < format.length()) {
+ int next = format.indexOf("{}", index);
+ if (next == -1) {
+ break;
+ }
+ sb.append(format.subSequence(index, next));
+ sb.append(argIndex < arguments.length ? String.valueOf(arguments[argIndex]) : "{}");
+ index = next + 2;
+ ++argIndex;
+ }
+ sb.append(format.substring(index));
+ System.err.println(sb);
+ }
+
+ @Override
+ public void info(String msg) {
+ info(msg, new Object[0]);
+ }
+
+ @Override
+ public void info(String format, Object arg) {
+ info(format, new Object[] { arg });
+ }
+
+ @Override
+ public void info(String format, Object arg1, Object arg2) {
+ info(format, new Object[] { arg1, arg2 });
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+ log("INFO", format, arguments);
+ }
+
+ @Override
+ public void info(String msg, Throwable t) {
+ info(msg);
+ }
+
+ @Override
+ public boolean isInfoEnabled(Marker marker) {
+ return true;
+ }
+
+ @Override
+ public void info(Marker marker, String msg) {
+ info(msg);
+ }
+
+ @Override
+ public void info(Marker marker, String format, Object arg) {
+ info(format, arg);
+ }
+
+ @Override
+ public void info(Marker marker, String format, Object arg1, Object arg2) {
+ info(format, arg1, arg2);
+ }
+
+ @Override
+ public void info(Marker marker, String format, Object... arguments) {
+ info(format, arguments);
+ }
+
+ @Override
+ public void info(Marker marker, String msg, Throwable t) {
+ info(msg, t);
+ }
+
+ @Override
+ public boolean isWarnEnabled() {
+ return true;
+ }
+
+ @Override
+ public void warn(String msg) {
+ warn(msg, new Object[0]);
+ }
+
+ @Override
+ public void warn(String format, Object arg) {
+ warn(format, new Object[] { arg });
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+ log("WARN", format, arguments);
+ }
+
+ @Override
+ public void warn(String format, Object arg1, Object arg2) {
+ warn(format, new Object[] { arg1, arg2 });
+ }
+
+ @Override
+ public void warn(String msg, Throwable t) {
+ warn(msg);
+ }
+
+ @Override
+ public boolean isWarnEnabled(Marker marker) {
+ return true;
+ }
+
+ @Override
+ public void warn(Marker marker, String msg) {
+ warn(msg);
+ }
+
+ @Override
+ public void warn(Marker marker, String format, Object arg) {
+ warn(format, arg);
+ }
+
+ @Override
+ public void warn(Marker marker, String format, Object arg1, Object arg2) {
+ warn(format, arg1, arg2);
+ }
+
+ @Override
+ public void warn(Marker marker, String format, Object... arguments) {
+ warn(format, arguments);
+ }
+
+ @Override
+ public void warn(Marker marker, String msg, Throwable t) {
+ warn(msg, t);
+ }
+
+ @Override
+ public boolean isErrorEnabled() {
+ return true;
+ }
+
+ @Override
+ public void error(String msg) {
+ error(msg, new Object[0]);
+ }
+
+ @Override
+ public void error(String format, Object arg) {
+ error(format, new Object[] { arg });
+ }
+
+ @Override
+ public void error(String format, Object arg1, Object arg2) {
+ error(format, new Object[] { arg1, arg2 });
+ }
+
+ @Override
+ public void error(String format, Object... arguments) {
+ log("ERRO", format, arguments);
+ }
+
+ @Override
+ public void error(String msg, Throwable t) {
+ error(msg);
+ }
+
+ @Override
+ public boolean isErrorEnabled(Marker marker) {
+ return true;
+ }
+
+ @Override
+ public void error(Marker marker, String msg) {
+ error(msg);
+ }
+
+ @Override
+ public void error(Marker marker, String format, Object arg) {
+ error(format, arg);
+ }
+
+ @Override
+ public void error(Marker marker, String format, Object arg1, Object arg2) {
+ error(format, arg1, arg2);
+ }
+
+ @Override
+ public void error(Marker marker, String format, Object... arguments) {
+ error(format, arguments);
+ }
+
+ @Override
+ public void error(Marker marker, String msg, Throwable t) {
+ error(msg, t);
+ }
+}
diff --git a/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/TeaVMLoggerFactory.java b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/TeaVMLoggerFactory.java
new file mode 100644
index 000000000..5eaf09800
--- /dev/null
+++ b/teavm-extras-slf4j/src/main/java/org/teavm/extras/slf4j/TeaVMLoggerFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 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.extras.slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.slf4j.ILoggerFactory;
+import org.slf4j.Logger;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class TeaVMLoggerFactory implements ILoggerFactory {
+ private Map loggers = new HashMap<>();
+
+ @Override
+ public Logger getLogger(String name) {
+ TeaVMLogger logger = loggers.get(name);
+ if (logger == null) {
+ logger = new TeaVMLogger(name);
+ loggers.put(name, logger);
+ }
+ return logger;
+ }
+}
\ No newline at end of file
diff --git a/teavm-extras-slf4j/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin b/teavm-extras-slf4j/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin
new file mode 100644
index 000000000..e045db3fd
--- /dev/null
+++ b/teavm-extras-slf4j/src/main/resources/META-INF/services/org.teavm.vm.spi.TeaVMPlugin
@@ -0,0 +1 @@
+org.teavm.extras.slf4j.Slf4jPlugin
\ No newline at end of file
diff --git a/teavm-samples/teavm-samples-async/pom.xml b/teavm-samples/teavm-samples-async/pom.xml
index 23133d3ec..99a38de06 100644
--- a/teavm-samples/teavm-samples-async/pom.xml
+++ b/teavm-samples/teavm-samples-async/pom.xml
@@ -33,6 +33,7 @@
org.teavm
teavm-classlib
${project.version}
+ provided