Allow interruption during dependency and linking phases. Use buffered

writer to render final JavaScript
This commit is contained in:
konsoletyper 2014-09-16 21:47:32 +04:00
parent ac2ffa42ff
commit 700d50b110
7 changed files with 200 additions and 38 deletions

View File

@ -48,6 +48,8 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
List<DependencyType> types = new ArrayList<>();
Map<String, DependencyType> typeMap = new HashMap<>();
private DependencyViolations dependencyViolations;
private DependencyCheckerInterruptor interruptor;
private boolean interrupted;
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) {
this.classSource = new DependencyClassSource(classSource);
@ -122,6 +124,18 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
});
}
public DependencyCheckerInterruptor getInterruptor() {
return interruptor;
}
public void setInterruptor(DependencyCheckerInterruptor interruptor) {
this.interruptor = interruptor;
}
public boolean wasInterrupted() {
return interrupted;
}
@Override
public DependencyType getType(String name) {
DependencyType type = typeMap.get(name);
@ -434,8 +448,17 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
}
public void processDependencies() {
interrupted = false;
int index = 0;
while (!tasks.isEmpty()) {
tasks.poll().run();
if (++index == 100) {
if (interruptor != null && !interruptor.shouldContinue()) {
interrupted = true;
break;
}
index = 0;
}
}
}

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.dependency;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface DependencyCheckerInterruptor {
boolean shouldContinue();
}

View File

@ -19,24 +19,13 @@ import org.teavm.model.*;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.util.ModelUtils;
/**
*
* @author Alexey Andreev
*/
public class Linker {
public ListableClassHolderSource link(DependencyInfo dependency) {
MutableClassHolderSource cutClasses = new MutableClassHolderSource();
for (String className : dependency.getAchievableClasses()) {
ClassHolder cls = ModelUtils.copyClass(dependency.getClassSource().get(className));
cutClasses.putClassHolder(cls);
link(dependency, cls);
}
return cutClasses;
}
private void link(DependencyInfo dependency, ClassHolder cls) {
public void link(DependencyInfo dependency, ClassHolder cls) {
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor());
MethodDependencyInfo methodDep = dependency.getMethod(methodRef);

View File

@ -16,9 +16,7 @@
package org.teavm.tooling;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.teavm.cache.DiskCachedClassHolderSource;
import org.teavm.cache.DiskProgramCache;
@ -192,6 +190,10 @@ public class TeaVMTool {
return cancelled;
}
public Collection<String> getClasses() {
return vm != null ? vm.getClasses() : Collections.<String>emptyList();
}
public void generate() throws TeaVMToolException {
try {
cancelled = false;
@ -263,7 +265,8 @@ public class TeaVMTool {
}
}
targetDirectory.mkdirs();
try (FileWriter writer = new FileWriter(new File(targetDirectory, targetFileName))) {
try (Writer writer = new OutputStreamWriter(new BufferedOutputStream(
new FileOutputStream(new File(targetDirectory, targetFileName))), "UTF-8")) {
if (runtime == RuntimeCopyOperation.MERGED) {
vm.add(runtimeInjector);
}

View File

@ -28,6 +28,7 @@ import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.Injector;
import org.teavm.model.*;
import org.teavm.model.util.ListingBuilder;
import org.teavm.model.util.ModelUtils;
import org.teavm.model.util.ProgramUtils;
import org.teavm.model.util.RegisterAllocator;
import org.teavm.optimization.*;
@ -307,6 +308,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return dependencyChecker.getDependencyViolations();
}
public Collection<String> getClasses() {
return dependencyChecker.getAchievableClasses();
}
/**
* <p>After building checks whether the build has failed due to some missing items (classes, methods and fields).
* If it has failed, throws exception, containing report on all missing items.
@ -343,23 +348,26 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return;
}
AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider();
dependencyChecker.linkMethod(new MethodReference("java.lang.Class", "createNew",
ValueType.object("java.lang.Class")), DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference("java.lang.String", "<init>",
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID), DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference("java.lang.String", "getChars",
ValueType.INTEGER, ValueType.INTEGER, ValueType.arrayOf(ValueType.CHARACTER), ValueType.INTEGER,
ValueType.VOID), DependencyStack.ROOT).use();
MethodDependency internDep = dependencyChecker.linkMethod(new MethodReference("java.lang.String", "intern",
ValueType.object("java.lang.String")), DependencyStack.ROOT);
dependencyChecker.setInterruptor(new DependencyCheckerInterruptor() {
@Override public boolean shouldContinue() {
return progressListener.progressReached(0) == TeaVMProgressFeedback.CONTINUE;
}
});
dependencyChecker.linkMethod(new MethodReference(Class.class, "createNew", Class.class),
DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class),
DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference(String.class, "getChars", int.class, int.class, char[].class,
int.class, void.class), DependencyStack.ROOT).use();
MethodDependency internDep = dependencyChecker.linkMethod(new MethodReference(String.class, "intern",
String.class), DependencyStack.ROOT);
internDep.getVariable(0).propagate(dependencyChecker.getType("java.lang.String"));
internDep.use();
dependencyChecker.linkMethod(new MethodReference("java.lang.String", "length", ValueType.INTEGER),
dependencyChecker.linkMethod(new MethodReference(String.class, "length", int.class),
DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference(Object.class, "clone", Object.class),
DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference("java.lang.Object", new MethodDescriptor("clone",
ValueType.object("java.lang.Object"))), DependencyStack.ROOT).use();
dependencyChecker.processDependencies();
reportProgress(1);
if (wasCancelled() || hasMissingItems()) {
return;
}
@ -369,9 +377,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (wasCancelled()) {
return;
}
Linker linker = new Linker();
ListableClassHolderSource classSet = linker.link(dependencyChecker);
reportProgress(1);
ListableClassHolderSource classSet = link(dependencyChecker);
if (wasCancelled()) {
return;
}
@ -449,6 +455,23 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
}
public ListableClassHolderSource link(DependencyInfo dependency) {
reportPhase(TeaVMPhase.LINKING, dependency.getAchievableClasses().size());
Linker linker = new Linker();
MutableClassHolderSource cutClasses = new MutableClassHolderSource();
if (wasCancelled()) {
return cutClasses;
}
int index = 0;
for (String className : dependency.getAchievableClasses()) {
ClassHolder cls = ModelUtils.copyClass(dependency.getClassSource().get(className));
cutClasses.putClassHolder(cls);
linker.link(dependency, cls);
progressListener.progressReached(++index);
}
return cutClasses;
}
private void reportPhase(TeaVMPhase phase, int progressLimit) {
if (progressListener.phaseStarted(phase, progressLimit) == TeaVMProgressFeedback.CANCEL) {
cancelled = true;

View File

@ -10,7 +10,9 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.teavm.dependency.*;
import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference;
@ -26,9 +28,15 @@ import org.teavm.tooling.TeaVMToolException;
public class TeaVMBuilder extends IncrementalProjectBuilder {
private URL[] classPath;
private IContainer[] sourceContainers;
private IContainer[] classFileContainers;
private Set<IProject> usedProjects = new HashSet<>();
@Override
protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
if ((kind == AUTO_BUILD || kind == INCREMENTAL_BUILD) && !shouldBuild()) {
System.out.println("Skipping project " + getProject().getName());
return null;
}
TeaVMProjectSettings projectSettings = getProjectSettings();
projectSettings.load();
TeaVMTool tool = new TeaVMTool();
@ -49,13 +57,59 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
if (tool.getDependencyViolations().hasMissingItems()) {
putMarkers(tool.getDependencyViolations());
}
TeaVMEclipsePlugin.getDefault().setProjectClasses(getProject(), classesToResources(tool.getClasses()));
if (!monitor.isCanceled()) {
monitor.done();
}
} catch (TeaVMToolException e) {
throw new CoreException(TeaVMEclipsePlugin.makeError(e));
} finally {
sourceContainers = null;
classFileContainers = null;
classPath = null;
usedProjects.clear();
}
return null;
return !usedProjects.isEmpty() ? usedProjects.toArray(new IProject[0]) : null;
}
private Set<String> classesToResources(Collection<String> classNames) {
Set<String> resourcePaths = new HashSet<>();
for (String className : classNames) {
for (IContainer clsContainer : classFileContainers) {
IResource res = clsContainer.findMember(className.replace('.', '/') + ".class");
if (res != null) {
resourcePaths.add(res.getFullPath().toString());
usedProjects.add(res.getProject());
}
}
}
return resourcePaths;
}
private boolean shouldBuild() throws CoreException {
Set<String> classes = TeaVMEclipsePlugin.getDefault().getProjectClasses(getProject());
if (classes.isEmpty()) {
return true;
}
for (IProject project : getRelatedProjects()) {
IResourceDelta delta = getDelta(project);
if (shouldBuild(classes, delta)) {
return true;
}
}
return false;
}
private boolean shouldBuild(Set<String> classes, IResourceDelta delta) {
if (classes.contains(delta.getResource().getFullPath().toString())) {
return true;
}
for (IResourceDelta child : delta.getAffectedChildren()) {
if (shouldBuild(classes, child)) {
return true;
}
}
return false;
}
private void removeMarkers() throws CoreException {
@ -200,6 +254,29 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
return new URLClassLoader(classPath, TeaVMBuilder.class.getClassLoader());
}
private Set<IProject> getRelatedProjects() throws CoreException {
Set<IProject> projects = new HashSet<>();
Set<IProject> visited = new HashSet<>();
Queue<IProject> queue = new ArrayDeque<>();
queue.add(getProject());
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
while (!queue.isEmpty()) {
IProject project = queue.remove();
if (!visited.add(project) || !project.hasNature(JavaCore.NATURE_ID)) {
continue;
}
projects.add(project);
IJavaProject javaProject = JavaCore.create(project);
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
project = (IProject)root.findMember(entry.getPath());
queue.add(project);
}
}
}
return projects;
}
private void prepareClassPath() throws CoreException {
classPath = new URL[0];
sourceContainers = new IContainer[0];
@ -210,9 +287,14 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
IJavaProject javaProject = JavaCore.create(project);
PathCollector collector = new PathCollector();
SourcePathCollector srcCollector = new SourcePathCollector();
SourcePathCollector binCollector = new SourcePathCollector();
IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot();
try {
collector.addPath(workspaceRoot.findMember(javaProject.getOutputLocation()).getLocation());
if (javaProject.getOutputLocation() != null) {
IContainer container = (IContainer)workspaceRoot.findMember(javaProject.getOutputLocation());
collector.addPath(container.getLocation());
binCollector.addContainer(container);
}
} catch (MalformedURLException e) {
TeaVMEclipsePlugin.logError(e);
}
@ -248,8 +330,10 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
IJavaProject depJavaProject = JavaCore.create(depProject);
if (depJavaProject.getOutputLocation() != null) {
try {
collector.addPath(workspaceRoot.findMember(depJavaProject.getOutputLocation())
.getLocation());
IContainer container = (IContainer)workspaceRoot.findMember(
depJavaProject.getOutputLocation());
collector.addPath(container.getLocation());
binCollector.addContainer(container);
} catch (MalformedURLException e) {
TeaVMEclipsePlugin.logError(e);
}
@ -260,6 +344,7 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
}
classPath = collector.getUrls();
sourceContainers = srcCollector.getContainers();
classFileContainers = binCollector.getContainers();
}
static class PathCollector {

View File

@ -16,7 +16,7 @@
package org.teavm.eclipse;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.core.resources.IProject;
@ -41,6 +41,7 @@ public class TeaVMEclipsePlugin extends AbstractUIPlugin {
public static final String DEPENDENCY_MARKER_ID = ID + ".dependencyMarker";
private static TeaVMEclipsePlugin defaultInstance;
private ConcurrentMap<IProject, TeaVMProjectSettings> settingsMap = new ConcurrentHashMap<>();
private Map<IProject, Set<String>> projectClasses = new WeakHashMap<>();
public TeaVMEclipsePlugin() {
defaultInstance = this;
@ -131,4 +132,18 @@ public class TeaVMEclipsePlugin extends AbstractUIPlugin {
}
}
}
public void setProjectClasses(IProject project, Set<String> classes) {
synchronized (projectClasses) {
projectClasses.put(project, new HashSet<>(classes));
}
}
public Set<String> getProjectClasses(IProject project) {
Set<String> classes;
synchronized (projectClasses) {
classes = projectClasses.get(project);
}
return classes != null ? new HashSet<>(classes) : new HashSet<String>();
}
}