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

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;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.teavm.codegen.*;
import org.teavm.common.FiniteExecutor;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyInformation;
import org.teavm.dependency.DependencyListener;
import org.teavm.javascript.ast.ClassNode;
import org.teavm.model.*;
import org.teavm.model.util.*;
@ -34,8 +32,8 @@ import org.teavm.optimization.Devirtualization;
*
* @author Alexey Andreev
*/
public class JavascriptBuilder {
private ClassHolderSource classSource;
public class JavascriptBuilder implements JavascriptBuilderHost {
private JavascriptProcessedClassSource classSource;
private DependencyChecker dependencyChecker;
private FiniteExecutor executor;
private ClassLoader classLoader;
@ -44,6 +42,7 @@ public class JavascriptBuilder {
private OutputStream logStream = System.out;
private Map<String, JavascriptEntryPoint> entryPoints = new HashMap<>();
private Map<String, String> exportedClasses = new HashMap<>();
private List<JavascriptResourceRenderer> ressourceRenderers = new ArrayList<>();
JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = new JavascriptProcessedClassSource(classSource);
@ -52,6 +51,21 @@ public class JavascriptBuilder {
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() {
return minifying;
}
@ -91,7 +105,7 @@ public class JavascriptBuilder {
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();
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, classSource);
naming.setMinifying(minifying);
@ -134,6 +148,9 @@ public class JavascriptBuilder {
sourceWriter.append(entry.getKey()).ws().append("=").ws().appendClass(entry.getValue()).append(";")
.softNewLine();
}
for (JavascriptResourceRenderer resourceRenderer : ressourceRenderers) {
resourceRenderer.render(target);
}
} catch (IOException e) {
throw new RenderingException("IO Error occured", e);
}
@ -275,13 +292,19 @@ public class JavascriptBuilder {
}
}
public void build(File file) throws RenderingException {
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
build(writer);
public void build(File dir, String fileName) throws RenderingException {
try (Writer writer = new OutputStreamWriter(new FileOutputStream(new File(dir, fileName)), "UTF-8")) {
build(writer, new DirectoryBuildTarget(dir));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Platform does not support UTF-8", e);
} catch (IOException 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;
import java.util.ArrayList;
import java.util.List;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.MethodHolder;
/**
*
* @author Alexey Andreev
*/
public class JavascriptProcessedClassSource implements ClassHolderSource {
class JavascriptProcessedClassSource implements ClassHolderSource {
private ThreadLocal<JavascriptNativeProcessor> processor = new ThreadLocal<>();
private ClassHolderSource innerSource;
private List<ClassHolderTransformer> transformers = new ArrayList<>();
public JavascriptProcessedClassSource(ClassHolderSource innerSource) {
this.innerSource = innerSource;
}
public void addTransformer(ClassHolderTransformer transformer) {
transformers.add(transformer);
}
@Override
public ClassHolder get(String name) {
ClassHolder cls = innerSource.get(name);
@ -48,6 +56,9 @@ public class JavascriptProcessedClassSource implements ClassHolderSource {
processor.processProgram(method.getProgram());
}
}
for (ClassHolderTransformer transformer : transformers) {
transformer.transformClass(cls);
}
}
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.SimpleFiniteExecutor;
import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.javascript.DirectoryBuildTarget;
import org.teavm.javascript.JavascriptBuilder;
import org.teavm.javascript.JavascriptBuilderFactory;
import org.teavm.model.*;
@ -234,6 +235,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
builderFactory.setExecutor(executor);
JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying);
builder.installPlugins();
File file = new File(outputDir, targetName);
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
MethodReference cons = new MethodReference(methodRef.getClassName(),
@ -241,7 +243,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
builder.entryPoint("initInstance", cons);
builder.entryPoint("runTest", methodRef).withValue(0, cons.getClassName());
builder.exportType("TestClass", cons.getClassName());
builder.build(innerWriter);
builder.build(innerWriter, new DirectoryBuildTarget(outputDir));
innerWriter.append("\n");
innerWriter.append("\nJUnitClient.run();");
innerWriter.close();

View File

@ -132,12 +132,13 @@ public class BuildJavascriptMojo extends AbstractMojo {
JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying);
builder.setBytecodeLogging(bytecodeLogging);
builder.installPlugins();
MethodDescriptor mainMethodDesc = new MethodDescriptor("main", ValueType.arrayOf(
ValueType.object("java.lang.String")), ValueType.VOID);
builder.entryPoint("main", new MethodReference(mainClass, mainMethodDesc))
.withValue(1, "java.lang.String");
targetDirectory.mkdirs();
builder.build(new File(targetDirectory, targetFileName));
builder.build(targetDirectory, targetFileName);
log.info("JavaScript file successfully built");
if (!runtimeSuppressed) {
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 {
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))) {
IOUtils.copy(input, output);
}