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<>(); List<DependencyType> types = new ArrayList<>();
Map<String, DependencyType> typeMap = new HashMap<>(); Map<String, DependencyType> typeMap = new HashMap<>();
private DependencyViolations dependencyViolations; private DependencyViolations dependencyViolations;
private DependencyCheckerInterruptor interruptor;
private boolean interrupted;
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) { public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) {
this.classSource = new DependencyClassSource(classSource); 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 @Override
public DependencyType getType(String name) { public DependencyType getType(String name) {
DependencyType type = typeMap.get(name); DependencyType type = typeMap.get(name);
@ -434,8 +448,17 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
} }
public void processDependencies() { public void processDependencies() {
interrupted = false;
int index = 0;
while (!tasks.isEmpty()) { while (!tasks.isEmpty()) {
tasks.poll().run(); 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.GetFieldInstruction;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction; import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.util.ModelUtils;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class Linker { public class Linker {
public ListableClassHolderSource link(DependencyInfo dependency) { public void link(DependencyInfo dependency, ClassHolder cls) {
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) {
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor()); MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor());
MethodDependencyInfo methodDep = dependency.getMethod(methodRef); MethodDependencyInfo methodDep = dependency.getMethod(methodRef);

View File

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

View File

@ -28,6 +28,7 @@ import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.Injector; import org.teavm.javascript.ni.Injector;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.util.ListingBuilder; import org.teavm.model.util.ListingBuilder;
import org.teavm.model.util.ModelUtils;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
import org.teavm.model.util.RegisterAllocator; import org.teavm.model.util.RegisterAllocator;
import org.teavm.optimization.*; import org.teavm.optimization.*;
@ -307,6 +308,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return dependencyChecker.getDependencyViolations(); 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). * <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. * If it has failed, throws exception, containing report on all missing items.
@ -343,23 +348,26 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return; return;
} }
AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider(); AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider();
dependencyChecker.linkMethod(new MethodReference("java.lang.Class", "createNew", dependencyChecker.setInterruptor(new DependencyCheckerInterruptor() {
ValueType.object("java.lang.Class")), DependencyStack.ROOT).use(); @Override public boolean shouldContinue() {
dependencyChecker.linkMethod(new MethodReference("java.lang.String", "<init>", return progressListener.progressReached(0) == TeaVMProgressFeedback.CONTINUE;
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, dependencyChecker.linkMethod(new MethodReference(Class.class, "createNew", Class.class),
ValueType.VOID), DependencyStack.ROOT).use(); DependencyStack.ROOT).use();
MethodDependency internDep = dependencyChecker.linkMethod(new MethodReference("java.lang.String", "intern", dependencyChecker.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class),
ValueType.object("java.lang.String")), DependencyStack.ROOT); 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.getVariable(0).propagate(dependencyChecker.getType("java.lang.String"));
internDep.use(); 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(); DependencyStack.ROOT).use();
dependencyChecker.linkMethod(new MethodReference("java.lang.Object", new MethodDescriptor("clone",
ValueType.object("java.lang.Object"))), DependencyStack.ROOT).use();
dependencyChecker.processDependencies(); dependencyChecker.processDependencies();
reportProgress(1);
if (wasCancelled() || hasMissingItems()) { if (wasCancelled() || hasMissingItems()) {
return; return;
} }
@ -369,9 +377,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (wasCancelled()) { if (wasCancelled()) {
return; return;
} }
Linker linker = new Linker(); ListableClassHolderSource classSet = link(dependencyChecker);
ListableClassHolderSource classSet = linker.link(dependencyChecker);
reportProgress(1);
if (wasCancelled()) { if (wasCancelled()) {
return; 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) { private void reportPhase(TeaVMPhase phase, int progressLimit) {
if (progressListener.phaseStarted(phase, progressLimit) == TeaVMProgressFeedback.CANCEL) { if (progressListener.phaseStarted(phase, progressLimit) == TeaVMProgressFeedback.CANCEL) {
cancelled = true; 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.IPath;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.variables.VariablesPlugin; 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.dependency.*;
import org.teavm.model.InstructionLocation; import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -26,9 +28,15 @@ import org.teavm.tooling.TeaVMToolException;
public class TeaVMBuilder extends IncrementalProjectBuilder { public class TeaVMBuilder extends IncrementalProjectBuilder {
private URL[] classPath; private URL[] classPath;
private IContainer[] sourceContainers; private IContainer[] sourceContainers;
private IContainer[] classFileContainers;
private Set<IProject> usedProjects = new HashSet<>();
@Override @Override
protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException { 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(); TeaVMProjectSettings projectSettings = getProjectSettings();
projectSettings.load(); projectSettings.load();
TeaVMTool tool = new TeaVMTool(); TeaVMTool tool = new TeaVMTool();
@ -49,13 +57,59 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
if (tool.getDependencyViolations().hasMissingItems()) { if (tool.getDependencyViolations().hasMissingItems()) {
putMarkers(tool.getDependencyViolations()); putMarkers(tool.getDependencyViolations());
} }
TeaVMEclipsePlugin.getDefault().setProjectClasses(getProject(), classesToResources(tool.getClasses()));
if (!monitor.isCanceled()) { if (!monitor.isCanceled()) {
monitor.done(); monitor.done();
} }
} catch (TeaVMToolException e) { } catch (TeaVMToolException e) {
throw new CoreException(TeaVMEclipsePlugin.makeError(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 { private void removeMarkers() throws CoreException {
@ -200,6 +254,29 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
return new URLClassLoader(classPath, TeaVMBuilder.class.getClassLoader()); 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 { private void prepareClassPath() throws CoreException {
classPath = new URL[0]; classPath = new URL[0];
sourceContainers = new IContainer[0]; sourceContainers = new IContainer[0];
@ -210,9 +287,14 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
IJavaProject javaProject = JavaCore.create(project); IJavaProject javaProject = JavaCore.create(project);
PathCollector collector = new PathCollector(); PathCollector collector = new PathCollector();
SourcePathCollector srcCollector = new SourcePathCollector(); SourcePathCollector srcCollector = new SourcePathCollector();
SourcePathCollector binCollector = new SourcePathCollector();
IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot(); IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot();
try { 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) { } catch (MalformedURLException e) {
TeaVMEclipsePlugin.logError(e); TeaVMEclipsePlugin.logError(e);
} }
@ -248,8 +330,10 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
IJavaProject depJavaProject = JavaCore.create(depProject); IJavaProject depJavaProject = JavaCore.create(depProject);
if (depJavaProject.getOutputLocation() != null) { if (depJavaProject.getOutputLocation() != null) {
try { try {
collector.addPath(workspaceRoot.findMember(depJavaProject.getOutputLocation()) IContainer container = (IContainer)workspaceRoot.findMember(
.getLocation()); depJavaProject.getOutputLocation());
collector.addPath(container.getLocation());
binCollector.addContainer(container);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
TeaVMEclipsePlugin.logError(e); TeaVMEclipsePlugin.logError(e);
} }
@ -260,6 +344,7 @@ public class TeaVMBuilder extends IncrementalProjectBuilder {
} }
classPath = collector.getUrls(); classPath = collector.getUrls();
sourceContainers = srcCollector.getContainers(); sourceContainers = srcCollector.getContainers();
classFileContainers = binCollector.getContainers();
} }
static class PathCollector { static class PathCollector {

View File

@ -16,7 +16,7 @@
package org.teavm.eclipse; package org.teavm.eclipse;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Arrays; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
@ -41,6 +41,7 @@ public class TeaVMEclipsePlugin extends AbstractUIPlugin {
public static final String DEPENDENCY_MARKER_ID = ID + ".dependencyMarker"; public static final String DEPENDENCY_MARKER_ID = ID + ".dependencyMarker";
private static TeaVMEclipsePlugin defaultInstance; private static TeaVMEclipsePlugin defaultInstance;
private ConcurrentMap<IProject, TeaVMProjectSettings> settingsMap = new ConcurrentHashMap<>(); private ConcurrentMap<IProject, TeaVMProjectSettings> settingsMap = new ConcurrentHashMap<>();
private Map<IProject, Set<String>> projectClasses = new WeakHashMap<>();
public TeaVMEclipsePlugin() { public TeaVMEclipsePlugin() {
defaultInstance = this; 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>();
}
} }