Adds pluggable system to extend TeaVM compiler

This commit is contained in:
konsoletyper 2014-02-13 17:22:25 +04:00
parent 8c4514a200
commit e13accc7e4
12 changed files with 277 additions and 15 deletions

View File

@ -15,8 +15,10 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.teavm.common.*; import org.teavm.common.*;
@ -38,6 +40,7 @@ public class DependencyChecker implements DependencyInformation {
private ConcurrentCachedMapper<FieldReference, DependencyNode> fieldCache; private ConcurrentCachedMapper<FieldReference, DependencyNode> fieldCache;
private ConcurrentMap<String, Object> achievableClasses = new ConcurrentHashMap<>(); private ConcurrentMap<String, Object> achievableClasses = new ConcurrentHashMap<>();
private ConcurrentMap<String, Object> initializedClasses = new ConcurrentHashMap<>(); private ConcurrentMap<String, Object> initializedClasses = new ConcurrentHashMap<>();
private List<DependencyListener> listeners = new ArrayList<>();
public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader) { public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader) {
this(classSource, classLoader, new SimpleFiniteExecutor()); this(classSource, classLoader, new SimpleFiniteExecutor());
@ -59,9 +62,19 @@ public class DependencyChecker implements DependencyInformation {
}); });
methodCache.addKeyListener(new KeyListener<MethodReference>() { methodCache.addKeyListener(new KeyListener<MethodReference>() {
@Override public void keyAdded(MethodReference key) { @Override public void keyAdded(MethodReference key) {
for (DependencyListener listener : listeners) {
listener.methodAchieved(DependencyChecker.this, key);
}
activateDependencyPlugin(key); activateDependencyPlugin(key);
} }
}); });
fieldCache.addKeyListener(new KeyListener<FieldReference>() {
@Override public void keyAdded(FieldReference key) {
for (DependencyListener listener : listeners) {
listener.fieldAchieved(DependencyChecker.this, key);
}
}
});
} }
public DependencyNode createNode() { public DependencyNode createNode() {
@ -72,6 +85,10 @@ public class DependencyChecker implements DependencyInformation {
return classSource; return classSource;
} }
public void addDependencyListener(DependencyListener listener) {
listeners.add(listener);
}
public void addEntryPoint(MethodReference methodRef, String... argumentTypes) { public void addEntryPoint(MethodReference methodRef, String... argumentTypes) {
ValueType[] parameters = methodRef.getDescriptor().getParameterTypes(); ValueType[] parameters = methodRef.getDescriptor().getParameterTypes();
if (parameters.length != argumentTypes.length) { if (parameters.length != argumentTypes.length) {
@ -98,7 +115,13 @@ public class DependencyChecker implements DependencyInformation {
} }
boolean achieveClass(String className) { boolean achieveClass(String className) {
return achievableClasses.putIfAbsent(className, dummyValue) == null; boolean result = achievableClasses.putIfAbsent(className, dummyValue) == null;
if (result) {
for (DependencyListener listener : listeners) {
listener.classAchieved(this, className);
}
}
return result;
} }
public MethodGraph attachMethodGraph(MethodReference methodRef) { public MethodGraph attachMethodGraph(MethodReference methodRef) {

View File

@ -0,0 +1,31 @@
/*
* 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.dependency;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public interface DependencyListener {
void classAchieved(DependencyChecker dependencyChecker, String className);
void methodAchieved(DependencyChecker dependencyChecker, MethodReference method);
void fieldAchieved(DependencyChecker dependencyChecker, FieldReference field);
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
*
* @author Alexey Andreev
*/
public class DirectoryBuildTarget implements JavascriptBuildTarget {
private File directory;
public DirectoryBuildTarget(File directory) {
this.directory = directory;
}
@Override
public OutputStream createResource(String fileName) throws IOException {
return new FileOutputStream(new File(directory, fileName));
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript;
import java.io.IOException;
import java.io.OutputStream;
/**
*
* @author Alexey Andreev
*/
public interface JavascriptBuildTarget {
OutputStream createResource(String fileName) throws IOException;
}

View File

@ -16,14 +16,12 @@
package org.teavm.javascript; package org.teavm.javascript;
import java.io.*; import java.io.*;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.codegen.*; import org.teavm.codegen.*;
import org.teavm.common.FiniteExecutor; import org.teavm.common.FiniteExecutor;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyInformation; import org.teavm.dependency.DependencyInformation;
import org.teavm.dependency.DependencyListener;
import org.teavm.javascript.ast.ClassNode; import org.teavm.javascript.ast.ClassNode;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.util.*; import org.teavm.model.util.*;
@ -34,8 +32,8 @@ import org.teavm.optimization.Devirtualization;
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class JavascriptBuilder { public class JavascriptBuilder implements JavascriptBuilderHost {
private ClassHolderSource classSource; private JavascriptProcessedClassSource classSource;
private DependencyChecker dependencyChecker; private DependencyChecker dependencyChecker;
private FiniteExecutor executor; private FiniteExecutor executor;
private ClassLoader classLoader; private ClassLoader classLoader;
@ -44,6 +42,7 @@ public class JavascriptBuilder {
private OutputStream logStream = System.out; private OutputStream logStream = System.out;
private Map<String, JavascriptEntryPoint> entryPoints = new HashMap<>(); private Map<String, JavascriptEntryPoint> entryPoints = new HashMap<>();
private Map<String, String> exportedClasses = new HashMap<>(); private Map<String, String> exportedClasses = new HashMap<>();
private List<JavascriptResourceRenderer> ressourceRenderers = new ArrayList<>();
JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) { JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = new JavascriptProcessedClassSource(classSource); this.classSource = new JavascriptProcessedClassSource(classSource);
@ -52,6 +51,21 @@ public class JavascriptBuilder {
this.executor = executor; this.executor = executor;
} }
@Override
public void add(DependencyListener listener) {
dependencyChecker.addDependencyListener(listener);
}
@Override
public void add(ClassHolderTransformer transformer) {
classSource.addTransformer(transformer);
}
@Override
public void add(JavascriptResourceRenderer resourceRenderer) {
ressourceRenderers.add(resourceRenderer);
}
public boolean isMinifying() { public boolean isMinifying() {
return minifying; return minifying;
} }
@ -91,7 +105,7 @@ public class JavascriptBuilder {
return classSource; return classSource;
} }
public void build(Appendable writer) throws RenderingException { public void build(Appendable writer, JavascriptBuildTarget target) throws RenderingException {
AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider(); AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider();
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, classSource); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, classSource);
naming.setMinifying(minifying); naming.setMinifying(minifying);
@ -134,6 +148,9 @@ public class JavascriptBuilder {
sourceWriter.append(entry.getKey()).ws().append("=").ws().appendClass(entry.getValue()).append(";") sourceWriter.append(entry.getKey()).ws().append("=").ws().appendClass(entry.getValue()).append(";")
.softNewLine(); .softNewLine();
} }
for (JavascriptResourceRenderer resourceRenderer : ressourceRenderers) {
resourceRenderer.render(target);
}
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO Error occured", e); throw new RenderingException("IO Error occured", e);
} }
@ -275,13 +292,19 @@ public class JavascriptBuilder {
} }
} }
public void build(File file) throws RenderingException { public void build(File dir, String fileName) throws RenderingException {
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { try (Writer writer = new OutputStreamWriter(new FileOutputStream(new File(dir, fileName)), "UTF-8")) {
build(writer); build(writer, new DirectoryBuildTarget(dir));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new RuntimeException("Platform does not support UTF-8", e); throw new RuntimeException("Platform does not support UTF-8", e);
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occured", e); throw new RenderingException("IO error occured", e);
} }
} }
public void installPlugins() {
for (JavascriptBuilderPlugin plugin : ServiceLoader.load(JavascriptBuilderPlugin.class)) {
plugin.install(this);
}
}
} }

View File

@ -0,0 +1,31 @@
/*
* 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.javascript;
import org.teavm.dependency.DependencyListener;
import org.teavm.model.ClassHolderTransformer;
/**
*
* @author Alexey Andreev
*/
public interface JavascriptBuilderHost {
void add(DependencyListener dependencyListener);
void add(ClassHolderTransformer classTransformer);
void add(JavascriptResourceRenderer resourceRenderer);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript;
/**
*
* @author Alexey Andreev
*/
public interface JavascriptBuilderPlugin {
void install(JavascriptBuilderHost host);
}

View File

@ -15,22 +15,30 @@
*/ */
package org.teavm.javascript; package org.teavm.javascript;
import java.util.ArrayList;
import java.util.List;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class JavascriptProcessedClassSource implements ClassHolderSource { class JavascriptProcessedClassSource implements ClassHolderSource {
private ThreadLocal<JavascriptNativeProcessor> processor = new ThreadLocal<>(); private ThreadLocal<JavascriptNativeProcessor> processor = new ThreadLocal<>();
private ClassHolderSource innerSource; private ClassHolderSource innerSource;
private List<ClassHolderTransformer> transformers = new ArrayList<>();
public JavascriptProcessedClassSource(ClassHolderSource innerSource) { public JavascriptProcessedClassSource(ClassHolderSource innerSource) {
this.innerSource = innerSource; this.innerSource = innerSource;
} }
public void addTransformer(ClassHolderTransformer transformer) {
transformers.add(transformer);
}
@Override @Override
public ClassHolder get(String name) { public ClassHolder get(String name) {
ClassHolder cls = innerSource.get(name); ClassHolder cls = innerSource.get(name);
@ -48,6 +56,9 @@ public class JavascriptProcessedClassSource implements ClassHolderSource {
processor.processProgram(method.getProgram()); processor.processProgram(method.getProgram());
} }
} }
for (ClassHolderTransformer transformer : transformers) {
transformer.transformClass(cls);
}
} }
private JavascriptNativeProcessor getProcessor() { private JavascriptNativeProcessor getProcessor() {

View File

@ -0,0 +1,26 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript;
import java.io.IOException;
/**
*
* @author Alexey Andreev
*/
public interface JavascriptResourceRenderer {
void render(JavascriptBuildTarget buildTarget) throws IOException;
}

View File

@ -0,0 +1,25 @@
/*
* 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.model;
/**
*
* @author Alexey Andreev
*/
public interface ClassHolderTransformer {
void transformClass(ClassHolder cls);
}

View File

@ -37,6 +37,7 @@ import org.junit.Test;
import org.teavm.common.FiniteExecutor; import org.teavm.common.FiniteExecutor;
import org.teavm.common.SimpleFiniteExecutor; import org.teavm.common.SimpleFiniteExecutor;
import org.teavm.common.ThreadPoolFiniteExecutor; import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.javascript.DirectoryBuildTarget;
import org.teavm.javascript.JavascriptBuilder; import org.teavm.javascript.JavascriptBuilder;
import org.teavm.javascript.JavascriptBuilderFactory; import org.teavm.javascript.JavascriptBuilderFactory;
import org.teavm.model.*; import org.teavm.model.*;
@ -234,6 +235,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
builderFactory.setExecutor(executor); builderFactory.setExecutor(executor);
JavascriptBuilder builder = builderFactory.create(); JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying); builder.setMinifying(minifying);
builder.installPlugins();
File file = new File(outputDir, targetName); File file = new File(outputDir, targetName);
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
MethodReference cons = new MethodReference(methodRef.getClassName(), MethodReference cons = new MethodReference(methodRef.getClassName(),
@ -241,7 +243,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
builder.entryPoint("initInstance", cons); builder.entryPoint("initInstance", cons);
builder.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()); builder.entryPoint("runTest", methodRef).withValue(0, cons.getClassName());
builder.exportType("TestClass", cons.getClassName()); builder.exportType("TestClass", cons.getClassName());
builder.build(innerWriter); builder.build(innerWriter, new DirectoryBuildTarget(outputDir));
innerWriter.append("\n"); innerWriter.append("\n");
innerWriter.append("\nJUnitClient.run();"); innerWriter.append("\nJUnitClient.run();");
innerWriter.close(); innerWriter.close();

View File

@ -132,12 +132,13 @@ public class BuildJavascriptMojo extends AbstractMojo {
JavascriptBuilder builder = builderFactory.create(); JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying); builder.setMinifying(minifying);
builder.setBytecodeLogging(bytecodeLogging); builder.setBytecodeLogging(bytecodeLogging);
builder.installPlugins();
MethodDescriptor mainMethodDesc = new MethodDescriptor("main", ValueType.arrayOf( MethodDescriptor mainMethodDesc = new MethodDescriptor("main", ValueType.arrayOf(
ValueType.object("java.lang.String")), ValueType.VOID); ValueType.object("java.lang.String")), ValueType.VOID);
builder.entryPoint("main", new MethodReference(mainClass, mainMethodDesc)) builder.entryPoint("main", new MethodReference(mainClass, mainMethodDesc))
.withValue(1, "java.lang.String"); .withValue(1, "java.lang.String");
targetDirectory.mkdirs(); targetDirectory.mkdirs();
builder.build(new File(targetDirectory, targetFileName)); builder.build(targetDirectory, targetFileName);
log.info("JavaScript file successfully built"); log.info("JavaScript file successfully built");
if (!runtimeSuppressed) { if (!runtimeSuppressed) {
resourceToFile("org/teavm/javascript/runtime.js", "runtime.js"); resourceToFile("org/teavm/javascript/runtime.js", "runtime.js");
@ -194,7 +195,7 @@ public class BuildJavascriptMojo extends AbstractMojo {
} }
private void resourceToFile(String resource, String fileName) throws IOException { private void resourceToFile(String resource, String fileName) throws IOException {
try (InputStream input = BuildJavascriptJUnitMojo.class.getClassLoader().getResourceAsStream(resource)) { try (InputStream input = BuildJavascriptMojo.class.getClassLoader().getResourceAsStream(resource)) {
try (OutputStream output = new FileOutputStream(new File(targetDirectory, fileName))) { try (OutputStream output = new FileOutputStream(new File(targetDirectory, fileName))) {
IOUtils.copy(input, output); IOUtils.copy(input, output);
} }