diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index f6af8d342..d21afc3eb 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -324,6 +324,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, naming, sourceWriter); renderer.setProperties(controller.getProperties()); renderer.setMinifying(minifying); + renderer.setProgressConsumer(controller::reportProgress); if (debugEmitter != null) { for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); @@ -352,7 +353,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { renderer.prepare(clsNodes); runtimeRenderer.renderRuntime(); - renderer.render(clsNodes); + if (!renderer.render(clsNodes)) { + return; + } renderer.renderStringPool(); renderer.renderStringConstants(); renderer.renderCompatibilityStubs(); diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java index 233723c55..352af216c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java @@ -52,20 +52,20 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getFullNameFor(MethodReference method) throws NamingException { + public String getFullNameFor(MethodReference method) { return getFullNameFor(method, 'M'); } @Override - public String getNameForInit(MethodReference method) throws NamingException { + public String getNameForInit(MethodReference method) { return getFullNameFor(method, 'I'); } - private String getFullNameFor(MethodReference method, char classifier) throws NamingException { + private String getFullNameFor(MethodReference method, char classifier) { MethodReference originalMethod = method; method = getRealMethod(method); if (method == null) { - throw new NamingException("Can't provide name for method as it was not found: " + originalMethod); + method = originalMethod; } MethodReference resolvedMethod = method; @@ -86,7 +86,7 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getFullNameFor(FieldReference field) throws NamingException { + public String getFullNameFor(FieldReference field) { String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName()); if (!realCls.equals(field.getClassName())) { String alias = getNameFor(new FieldReference(realCls, field.getFieldName())); @@ -99,12 +99,12 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getNameForFunction(String name) throws NamingException { + public String getNameForFunction(String name) { return functionAliases.computeIfAbsent(name, key -> aliasProvider.getFunctionAlias(key)); } @Override - public String getNameForClassInit(String className) throws NamingException { + public String getNameForClassInit(String className) { return classInitAliases.computeIfAbsent(className, key -> aliasProvider.getClassInitAlias(key)); } @@ -131,17 +131,16 @@ public class DefaultNamingStrategy implements NamingStrategy { String initialCls = cls; while (!fieldExists(cls, field)) { ClassReader clsHolder = classSource.get(cls); - cls = clsHolder.getParent(); - if (cls == null) { - throw new NamingException("Can't provide name for field as the field not found: " - + initialCls + "." + field); + if (clsHolder == null || clsHolder.getParent() == null) { + return initialCls; } + cls = clsHolder.getParent(); } return cls; } private boolean fieldExists(String cls, String field) { ClassReader classHolder = classSource.get(cls); - return classHolder.getField(field) != null; + return classHolder != null && classHolder.getField(field) != null; } } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingException.java b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingException.java deleted file mode 100644 index dcc262e3e..000000000 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2016 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.backend.javascript.codegen; - -public class NamingException extends RuntimeException { - private static final long serialVersionUID = 3472322553091962348L; - - public NamingException() { - super(); - } - - public NamingException(String message) { - super(message); - } -} diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java index 508e0b16f..e7f2ee79d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/NamingStrategy.java @@ -20,19 +20,19 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; public interface NamingStrategy { - String getNameFor(String cls) throws NamingException; + String getNameFor(String cls); - String getNameFor(MethodDescriptor method) throws NamingException; + String getNameFor(MethodDescriptor method); - String getNameForInit(MethodReference method) throws NamingException; + String getNameForInit(MethodReference method); - String getFullNameFor(MethodReference method) throws NamingException; + String getFullNameFor(MethodReference method); - String getNameFor(FieldReference field) throws NamingException; + String getNameFor(FieldReference field); - String getFullNameFor(FieldReference method) throws NamingException; + String getFullNameFor(FieldReference method); - String getNameForFunction(String name) throws NamingException; + String getNameForFunction(String name); - String getNameForClassInit(String className) throws NamingException; + String getNameForClassInit(String className); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java index 068622a42..3e14a850b 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java @@ -98,45 +98,43 @@ public class SourceWriter implements Appendable, LocationProvider { innerWriter.append(csq, start, end); } - public SourceWriter appendClass(String cls) throws NamingException, IOException { + public SourceWriter appendClass(String cls) throws IOException { return append(naming.getNameFor(cls)); } - public SourceWriter appendClass(Class cls) throws NamingException, IOException { + public SourceWriter appendClass(Class cls) throws IOException { return append(naming.getNameFor(cls.getName())); } - public SourceWriter appendField(FieldReference field) throws NamingException, IOException { + public SourceWriter appendField(FieldReference field) throws IOException { return append(naming.getNameFor(field)); } - public SourceWriter appendStaticField(FieldReference field) throws NamingException, IOException { + public SourceWriter appendStaticField(FieldReference field) throws IOException { return append(naming.getFullNameFor(field)); } - public SourceWriter appendMethod(MethodDescriptor method) throws NamingException, IOException { + public SourceWriter appendMethod(MethodDescriptor method) throws IOException { return append(naming.getNameFor(method)); } - public SourceWriter appendMethod(String name, Class... params) throws NamingException, IOException { + public SourceWriter appendMethod(String name, Class... params) throws IOException { return append(naming.getNameFor(new MethodDescriptor(name, params))); } - public SourceWriter appendMethodBody(MethodReference method) throws NamingException, IOException { + public SourceWriter appendMethodBody(MethodReference method) throws IOException { return append(naming.getFullNameFor(method)); } - public SourceWriter appendMethodBody(String className, String name, ValueType... params) - throws NamingException, IOException { + public SourceWriter appendMethodBody(String className, String name, ValueType... params) throws IOException { return append(naming.getFullNameFor(new MethodReference(className, new MethodDescriptor(name, params)))); } - public SourceWriter appendMethodBody(Class cls, String name, Class... params) - throws NamingException, IOException { + public SourceWriter appendMethodBody(Class cls, String name, Class... params) throws IOException { return append(naming.getFullNameFor(new MethodReference(cls, name, params))); } - public SourceWriter appendFunction(String name) throws NamingException, IOException { + public SourceWriter appendFunction(String name) throws IOException { return append(naming.getNameForFunction(name)); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index c368dd47e..8412e402a 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.function.IntFunction; import java.util.stream.Collectors; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; @@ -37,7 +38,6 @@ import org.teavm.ast.MethodNodeVisitor; import org.teavm.ast.NativeMethodNode; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.VariableNode; -import org.teavm.backend.javascript.codegen.NamingException; import org.teavm.backend.javascript.codegen.NamingOrderer; import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.SourceWriter; @@ -57,6 +57,7 @@ import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.vm.RenderingException; +import org.teavm.vm.TeaVMProgressFeedback; public class Renderer implements RenderingManager { private final NamingStrategy naming; @@ -72,6 +73,7 @@ public class Renderer implements RenderingManager { private final Diagnostics diagnostics; private RenderingContext context; private List postponedFieldInitializers = new ArrayList<>(); + private IntFunction progressConsumer = p -> TeaVMProgressFeedback.CONTINUE; private ObjectIntMap sizeByClass = new ObjectIntHashMap<>(); private int stringPoolSize; @@ -161,6 +163,10 @@ public class Renderer implements RenderingManager { this.debugEmitter = debugEmitter; } + public void setProgressConsumer(IntFunction progressConsumer) { + this.progressConsumer = progressConsumer; + } + public void setProperties(Properties properties) { this.properties.clear(); this.properties.putAll(properties); @@ -265,7 +271,7 @@ public class Renderer implements RenderingManager { } } - public void render(List classes) throws RenderingException { + public boolean render(List classes) throws RenderingException { if (minifying) { try { renderRuntimeAliases(); @@ -273,13 +279,18 @@ public class Renderer implements RenderingManager { throw new RenderingException(e); } } + int index = 0; for (ClassNode cls : classes) { int start = writer.getOffset(); renderDeclaration(cls); renderMethodBodies(cls); appendClassSize(cls.getName(), writer.getOffset() - start); + if (progressConsumer.apply(1000 * ++index / classes.size()) == TeaVMProgressFeedback.CANCEL) { + return false; + } } renderClassMetadata(classes); + return true; } private void renderDeclaration(ClassNode cls) throws RenderingException { @@ -337,8 +348,6 @@ public class Renderer implements RenderingManager { writer.append("var ").appendStaticField(fieldRef).ws().append("=").ws() .append(context.constantToString(value)).append(";").softNewLine(); } - } catch (NamingException e) { - throw new RenderingException("Error rendering class " + cls.getName() + ". See cause for details", e); } catch (IOException e) { throw new RenderingException("IO error occurred", e); } @@ -366,8 +375,6 @@ public class Renderer implements RenderingManager { for (MethodNode method : cls.getMethods()) { renderBody(method); } - } catch (NamingException e) { - throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e); } catch (IOException e) { throw new RenderingException("IO error occurred", e); } @@ -508,8 +515,6 @@ public class Renderer implements RenderingManager { renderVirtualDeclarations(virtualMethods); } writer.append("]);").newLine(); - } catch (NamingException e) { - throw new RenderingException("Error rendering class metadata. See a cause for details", e); } catch (IOException e) { throw new RenderingException("IO error occurred", e); } @@ -688,7 +693,7 @@ public class Renderer implements RenderingManager { return minifying ? RenderingUtil.indexToId(index) : "var_" + index; } - private void renderVirtualDeclarations(Collection methods) throws NamingException, IOException { + private void renderVirtualDeclarations(Collection methods) throws IOException { if (methods.stream().noneMatch(this::isVirtual)) { writer.append('0'); return; diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java index b96c1b151..c60be1bc3 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java @@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets; import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; import org.mozilla.javascript.ast.AstRoot; -import org.teavm.backend.javascript.codegen.NamingException; import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.model.ClassReader; @@ -73,8 +72,6 @@ public class RuntimeRenderer { renderRuntimeCreateException(); renderCreateStackTraceElement(); renderSetStackTrace(); - } catch (NamingException e) { - throw new RenderingException("Error rendering runtime methods. See a cause for details", e); } catch (IOException e) { throw new RenderingException("IO error", e); } diff --git a/core/src/main/java/org/teavm/cache/AlwaysFreshCacheStatus.java b/core/src/main/java/org/teavm/cache/AlwaysFreshCacheStatus.java deleted file mode 100644 index 7484134d8..000000000 --- a/core/src/main/java/org/teavm/cache/AlwaysFreshCacheStatus.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2018 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.cache; - -import org.teavm.model.MethodReference; - -public class AlwaysFreshCacheStatus implements CacheStatus { - public static final AlwaysFreshCacheStatus INSTANCE = new AlwaysFreshCacheStatus(); - - private AlwaysFreshCacheStatus() { - } - - @Override - public boolean isStaleClass(String className) { - return false; - } - - @Override - public boolean isStaleMethod(MethodReference method) { - return false; - } -} diff --git a/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java b/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java new file mode 100644 index 000000000..13b18515e --- /dev/null +++ b/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java @@ -0,0 +1,65 @@ +/* + * Copyright 2018 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.cache; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReference; + +public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheStatus { + private ClassReaderSource underlyingSource; + private final Map> cache = new HashMap<>(); + private final Set freshClasses = new HashSet<>(); + + public void setUnderlyingSource(ClassReaderSource underlyingSource) { + this.underlyingSource = underlyingSource; + } + + @Override + public boolean isStaleClass(String className) { + return !freshClasses.contains(className); + } + + @Override + public boolean isStaleMethod(MethodReference method) { + return isStaleClass(method.getClassName()); + } + + @Override + public ClassReader get(String name) { + return cache.computeIfAbsent(name, key -> { + if (underlyingSource == null) { + return Optional.empty(); + } + return Optional.ofNullable(underlyingSource.get(key)); + }).orElse(null); + } + + public void commit() { + freshClasses.addAll(cache.keySet()); + } + + public void evict(Collection classes) { + cache.keySet().removeAll(classes); + freshClasses.removeAll(classes); + } +} diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index 5a8d5a089..f742334eb 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -648,7 +648,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo { if (interrupted) { return; } - int index = 0; while (!deferredTasks.isEmpty() || !tasks.isEmpty() || !pendingTransitions.isEmpty()) { while (true) { processNodeToNodeTransitionQueue(); @@ -658,12 +657,9 @@ public abstract class DependencyAnalyzer implements DependencyInfo { while (!tasks.isEmpty()) { tasks.remove().run(); } - if (++index == 100) { - if (interruptor != null && !interruptor.shouldContinue()) { - interrupted = true; - break; - } - index = 0; + if (interruptor != null && !interruptor.shouldContinue()) { + interrupted = true; + return; } } diff --git a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java index 25721bfd4..bcd95a3e0 100644 --- a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java +++ b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java @@ -243,6 +243,7 @@ public class MissingItemsProcessor { public void visit(CastInstruction insn) { checkClass(insn.getLocation(), insn.getTargetType()); } + @Override public void visit(ClassConstantInstruction insn) { checkClass(insn.getLocation(), insn.getConstant()); diff --git a/core/src/main/java/org/teavm/vm/MemoryBuildTarget.java b/core/src/main/java/org/teavm/vm/MemoryBuildTarget.java new file mode 100644 index 000000000..60bf9c5a0 --- /dev/null +++ b/core/src/main/java/org/teavm/vm/MemoryBuildTarget.java @@ -0,0 +1,53 @@ +/* + * Copyright 2018 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.vm; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class MemoryBuildTarget implements BuildTarget { + private Map data = new LinkedHashMap<>(); + private Set names = Collections.unmodifiableSet(data.keySet()); + + public Set getNames() { + return names; + } + + public byte[] getContent(String name) { + ByteArrayOutputStream stream = data.get(name); + return stream != null ? stream.toByteArray() : null; + } + + public void clear() { + data.clear(); + } + + @Override + public OutputStream createResource(String fileName) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + data.put(fileName, stream); + return stream; + } + + public OutputStream appendToResource(String fileName) { + return data.computeIfAbsent(fileName, k -> new ByteArrayOutputStream()); + } +} diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 795df7ddb..40d8ad006 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -140,6 +140,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private AnnotationAwareCacheStatus cacheStatus; private ProgramDependencyExtractor programDependencyExtractor = new ProgramDependencyExtractor(); private List> additionalVirtualMethods = new ArrayList<>(); + private int lastKnownClasses; TeaVM(TeaVMBuilder builder) { target = builder.target; @@ -336,6 +337,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository { return writtenClasses; } + public void setLastKnownClasses(int lastKnownClasses) { + this.lastKnownClasses = lastKnownClasses; + } + /** *

Does actual build. Call this method after TeaVM is fully configured and all entry points * are specified. This method may fail if there are items (classes, methods and fields) @@ -351,13 +356,16 @@ public class TeaVM implements TeaVMHost, ServiceRepository { target.setController(targetController); // Check dependencies - reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, 1); + reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, lastKnownClasses > 0 ? lastKnownClasses : 1); if (wasCancelled()) { return; } dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported()); - dependencyAnalyzer.setInterruptor(() -> progressListener.progressReached(0) == TeaVMProgressFeedback.CONTINUE); + dependencyAnalyzer.setInterruptor(() -> { + int progress = lastKnownClasses > 0 ? dependencyAnalyzer.getReachableClasses().size() : 0; + return progressListener.progressReached(progress) == TeaVMProgressFeedback.CONTINUE; + }); target.contributeDependencies(dependencyAnalyzer); dependencyAnalyzer.processDependencies(); if (wasCancelled() || !diagnostics.getSevereProblems().isEmpty()) { @@ -368,7 +376,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository { cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass); // Link - reportPhase(TeaVMPhase.LINKING, 1); if (wasCancelled()) { return; } @@ -379,28 +386,36 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } // Optimize and allocate registers - reportPhase(TeaVMPhase.OPTIMIZATION, 1); + int maxOptimizationProgress = classSet.getClassNames().size(); + if (optimizationLevel == TeaVMOptimizationLevel.ADVANCED) { + maxOptimizationProgress *= 2; + } else if (optimizationLevel == TeaVMOptimizationLevel.FULL) { + maxOptimizationProgress *= 3; + } + reportPhase(TeaVMPhase.OPTIMIZATION, maxOptimizationProgress); + int progress = 0; if (optimizationLevel != TeaVMOptimizationLevel.SIMPLE) { - devirtualize(classSet, dependencyAnalyzer); + progress = devirtualize(progress, classSet, dependencyAnalyzer); if (wasCancelled()) { return; } } dependencyAnalyzer.cleanup(); - inline(classSet, dependencyAnalyzer); + progress = inline(progress, classSet, dependencyAnalyzer); if (wasCancelled()) { return; } - optimize(classSet); + optimize(progress, classSet); if (wasCancelled()) { return; } // Render try { + reportPhase(TeaVMPhase.RENDERING, 1000); target.emit(classSet, buildTarget, outputName); } catch (IOException e) { throw new RuntimeException("Error generating output files", e); @@ -442,28 +457,31 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } } - private void devirtualize(ListableClassHolderSource classes, DependencyInfo dependency) { + private int devirtualize(int progress, ListableClassHolderSource classes, DependencyInfo dependency) { if (wasCancelled()) { - return; + return progress; } Devirtualization devirtualization = new Devirtualization(dependency, classes); + int index = 0; for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); - for (final MethodHolder method : cls.getMethods()) { + for (MethodHolder method : cls.getMethods()) { if (method.getProgram() != null) { devirtualization.apply(method); } } + progressListener.progressReached(++index); if (wasCancelled()) { - return; + break; } } virtualMethods = devirtualization.getVirtualMethods(); + return progress; } - private void inline(ListableClassHolderSource classes, DependencyInfo dependencyInfo) { + private int inline(int progress, ListableClassHolderSource classes, DependencyInfo dependencyInfo) { if (optimizationLevel != TeaVMOptimizationLevel.FULL) { - return; + return progress; } Map inlinedPrograms = new HashMap<>(); @@ -479,8 +497,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository { inlinedPrograms.put(method.getReference(), program); } } + progressListener.progressReached(++progress); if (wasCancelled()) { - return; + break; } } @@ -492,18 +511,22 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } } } + + return progress; } - private void optimize(ListableClassHolderSource classSource) { + private int optimize(int progress, ListableClassHolderSource classSource) { for (String className : classSource.getClassNames()) { ClassHolder cls = classSource.get(className); for (MethodHolder method : cls.getMethods()) { processMethod(method, classSource); } + progressListener.progressReached(++progress); if (wasCancelled()) { - return; + break; } } + return progress; } private void processMethod(MethodHolder method, ListableClassReaderSource classSource) { @@ -700,5 +723,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository { public boolean isVirtual(MethodReference method) { return virtualMethods == null || virtualMethods.contains(method); } + + @Override + public TeaVMProgressFeedback reportProgress(int progres) { + return progressListener.progressReached(progres); + } }; } diff --git a/core/src/main/java/org/teavm/vm/TeaVMPhase.java b/core/src/main/java/org/teavm/vm/TeaVMPhase.java index 45f123116..1b6688099 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMPhase.java +++ b/core/src/main/java/org/teavm/vm/TeaVMPhase.java @@ -19,6 +19,5 @@ public enum TeaVMPhase { DEPENDENCY_ANALYSIS, LINKING, OPTIMIZATION, - DECOMPILATION, RENDERING } diff --git a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java index b78e906cf..9572f21af 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java @@ -49,4 +49,6 @@ public interface TeaVMTargetController { Set getPreservedClasses(); boolean isVirtual(MethodReference method); + + TeaVMProgressFeedback reportProgress(int progress); } diff --git a/pom.xml b/pom.xml index 14e06603a..86f27e4e7 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ UTF-8 https://oss.sonatype.org/content/repositories/snapshots/ 1.5 - 9.2.1.v20140609 + 9.4.14.v20181114 1.7.7 2.6.2 2017.3.5 @@ -93,6 +93,7 @@ tools/maven tools/chrome-rdp tools/junit + tools/devserver tests extras-slf4j metaprogramming/impl diff --git a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java index bfdc4ef11..78b207c0e 100644 --- a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java +++ b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java @@ -44,6 +44,7 @@ public final class BenchmarkStarter { private static double timeSpentCalculating; private static double totalTime; + private BenchmarkStarter() { } diff --git a/tools/chrome-rdp/pom.xml b/tools/chrome-rdp/pom.xml index d9d84ade4..bad58b2e5 100644 --- a/tools/chrome-rdp/pom.xml +++ b/tools/chrome-rdp/pom.xml @@ -50,15 +50,11 @@ javax.websocket javax.websocket-api + - org.codehaus.jackson - jackson-core-asl - 1.9.13 - - - org.codehaus.jackson - jackson-mapper-asl - 1.9.13 + com.fasterxml.jackson.core + jackson-databind + true diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java index 7366174b8..7b0778edb 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java @@ -15,6 +15,8 @@ */ package org.teavm.chromerdp; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -29,8 +31,6 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.map.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.teavm.chromerdp.data.CallArgumentDTO; @@ -149,7 +149,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC future.completeExceptionally(e); } } else { - Message message = mapper.reader(Message.class).readValue(messageText); + Message message = mapper.readerFor(Message.class).readValue(messageText); if (message.getMethod() == null) { return; } @@ -404,7 +404,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC CallFunctionResponse response = callMethod("Runtime.callFunctionOn", CallFunctionResponse.class, params); RemoteObjectDTO result = response != null ? response.getResult() : null; - return result.getValue() != null ? result.getValue().getTextValue() : null; + return result.getValue() != null ? result.getValue().textValue() : null; } String getRepresentation(String objectId) { @@ -417,7 +417,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC CallFunctionResponse response = callMethod("Runtime.callFunctionOn", CallFunctionResponse.class, params); RemoteObjectDTO result = response != null ? response.getResult() : null; - return result.getValue() != null ? result.getValue().getTextValue() : null; + return result.getValue() != null ? result.getValue().textValue() : null; } private List parseProperties(PropertyDescriptorDTO[] properties) { @@ -452,7 +452,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC } private T parseJson(Class type, JsonNode node) throws IOException { - return mapper.reader(type).readValue(node); + return mapper.readerFor(type).readValue(node); } private void sendMessage(Message message) { @@ -516,7 +516,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC if (node == null) { out.complete(null); } else { - R response = returnType != void.class ? mapper.reader(returnType).readValue(node) : null; + R response = returnType != void.class ? mapper.readerFor(returnType).readValue(node) : null; out.complete(response); } }); diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPServer.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPServer.java index 90d7499ae..9b1900417 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPServer.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPServer.java @@ -15,7 +15,10 @@ */ package org.teavm.chromerdp; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import javax.websocket.Decoder; import javax.websocket.Encoder; import javax.websocket.Extension; @@ -57,9 +60,8 @@ public class ChromeRDPServer { context.setContextPath("/"); server.setHandler(context); - ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); - try { + ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); wscontainer.addEndpoint(new RPDEndpointConfig()); server.start(); server.join(); diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java index c884536f9..16d6dccce 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java @@ -15,8 +15,8 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; @JsonIgnoreProperties(ignoreUnknown = true) public class CallArgumentDTO { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java index c792bdc29..afcca1979 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class CallFrameDTO { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/LocationDTO.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/LocationDTO.java index 1ce1b69bf..077c3684e 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/LocationDTO.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/LocationDTO.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class LocationDTO { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Message.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Message.java index 17d3c30a9..c3ecd6605 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Message.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Message.java @@ -15,8 +15,8 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; @JsonIgnoreProperties(ignoreUnknown = true) public class Message { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java index 1808ad0ce..28f157431 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class PropertyDescriptorDTO { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java index 7ad40f7e3..dda27174c 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java @@ -15,8 +15,8 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; @JsonIgnoreProperties(ignoreUnknown = true) public class RemoteObjectDTO { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Response.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Response.java index 9d3043a84..8cbc0902e 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Response.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/Response.java @@ -15,8 +15,8 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; @JsonIgnoreProperties(ignoreUnknown = true) public class Response { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/ScopeDTO.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/ScopeDTO.java index 1c5b5b4bf..e7e41a0e0 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/ScopeDTO.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/data/ScopeDTO.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.data; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class ScopeDTO { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java index 93cb39e2f..d17def3f3 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.teavm.chromerdp.data.CallArgumentDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java index 9f6178d8f..742ab7040 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.teavm.chromerdp.data.RemoteObjectDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptCommand.java index 0946fb9cc..5b13f0717 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class CompileScriptCommand { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptResponse.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptResponse.java index d83cb8478..b1e91d2ac 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptResponse.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CompileScriptResponse.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class CompileScriptResponse { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ContinueToLocationCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ContinueToLocationCommand.java index a23f1a897..a80bd72df 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ContinueToLocationCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ContinueToLocationCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.teavm.chromerdp.data.LocationDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java index 584c4554d..aec7c7612 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class GetPropertiesCommand { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java index 0b017c7de..a34e962cd 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.teavm.chromerdp.data.PropertyDescriptorDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RemoveBreakpointCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RemoveBreakpointCommand.java index 0b2818e11..ebaf3aa98 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RemoveBreakpointCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RemoveBreakpointCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class RemoveBreakpointCommand { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RunScriptCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RunScriptCommand.java index 340bff615..e89f7a371 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RunScriptCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/RunScriptCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class RunScriptCommand { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ScriptParsedNotification.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ScriptParsedNotification.java index 3681b91e0..d111c0e47 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ScriptParsedNotification.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/ScriptParsedNotification.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class ScriptParsedNotification { diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointCommand.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointCommand.java index ce63628b5..7965f82b5 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointCommand.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointCommand.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.teavm.chromerdp.data.LocationDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointResponse.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointResponse.java index 8e203f8db..136bf1702 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointResponse.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SetBreakpointResponse.java @@ -15,7 +15,7 @@ */ package org.teavm.chromerdp.messages; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.teavm.chromerdp.data.LocationDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SuspendedNotification.java b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SuspendedNotification.java index 0f562f41e..d38fcc2c4 100644 --- a/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SuspendedNotification.java +++ b/tools/chrome-rdp/src/main/java/org/teavm/chromerdp/messages/SuspendedNotification.java @@ -15,9 +15,9 @@ */ package org.teavm.chromerdp.messages; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; import java.util.List; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.teavm.chromerdp.data.CallFrameDTO; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml index 4a0b7cea8..678bc8fdd 100644 --- a/tools/cli/pom.xml +++ b/tools/cli/pom.xml @@ -51,6 +51,11 @@ ${project.version} runtime + + org.teavm + teavm-devserver + ${project.version} + org.teavm teavm-jso-impl diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java new file mode 100644 index 000000000..0298db148 --- /dev/null +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java @@ -0,0 +1,163 @@ +/* + * Copyright 2018 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.cli; + +import java.util.Arrays; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; +import org.teavm.devserver.DevServer; +import org.teavm.tooling.ConsoleTeaVMToolLog; + +public final class TeaVMDevServerRunner { + private static Options options = new Options(); + private ConsoleTeaVMToolLog log = new ConsoleTeaVMToolLog(false); + private DevServer devServer; + private CommandLine commandLine; + + static { + setupOptions(); + } + + @SuppressWarnings("static-access") + private static void setupOptions() { + options.addOption(OptionBuilder + .withArgName("directory") + .hasArg() + .withDescription("a directory, relative to server's root, which serves generated files") + .withLongOpt("targetdir") + .create('d')); + options.addOption(OptionBuilder + .withArgName("file") + .hasArg() + .withDescription("a file where to put decompiled classes (classes.js by default)") + .withLongOpt("targetfile") + .create('f')); + options.addOption(OptionBuilder + .withArgName("classpath") + .hasArg() + .withDescription("classpath element (either directory or jar file)") + .withLongOpt("classpath") + .create('p')); + options.addOption(OptionBuilder + .withArgName("sourcepath") + .hasArg() + .withDescription("source path (either directory or jar file which contains source code)") + .withLongOpt("sourcepath") + .create('s')); + options.addOption(OptionBuilder + .withArgName("number") + .hasArg() + .withDescription("port (default is 9090)") + .create("port")); + options.addOption(OptionBuilder + .withDescription("display indicator on web page") + .create("indicator")); + options.addOption(OptionBuilder + .withDescription("display more messages on server log") + .withLongOpt("verbose") + .create('v')); + } + + private TeaVMDevServerRunner(CommandLine commandLine) { + this.commandLine = commandLine; + devServer = new DevServer(); + } + + public static void main(String[] args) { + if (args.length == 0) { + printUsage(); + return; + } + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + try { + commandLine = parser.parse(options, args); + } catch (ParseException e) { + printUsage(); + return; + } + + TeaVMDevServerRunner runner = new TeaVMDevServerRunner(commandLine); + runner.parseArguments(); + runner.setUp(); + runner.runAll(); + } + + private void parseArguments() { + parseClassPathOptions(); + parseSourcePathOptions(); + parseOutputOptions(); + + devServer.setIndicator(commandLine.hasOption("indicator")); + if (commandLine.hasOption("port")) { + try { + devServer.setPort(Integer.parseInt(commandLine.getOptionValue("port"))); + } catch (NumberFormatException e) { + System.err.println("port must be numeric"); + printUsage(); + } + } + + String[] args = commandLine.getArgs(); + if (args.length != 1) { + System.err.println("Unexpected arguments"); + printUsage(); + } else if (args.length == 1) { + devServer.setMainClass(args[0]); + } + } + + private void parseOutputOptions() { + if (commandLine.hasOption("d")) { + devServer.setPathToFile(commandLine.getOptionValue("d")); + } + if (commandLine.hasOption("f")) { + devServer.setFileName(commandLine.getOptionValue("f")); + } + } + + private void parseClassPathOptions() { + if (commandLine.hasOption('p')) { + devServer.setClassPath(commandLine.getOptionValues('p')); + } + } + + private void parseSourcePathOptions() { + if (commandLine.hasOption('s')) { + devServer.getSourcePath().addAll(Arrays.asList(commandLine.getOptionValues('s'))); + } + } + + private void setUp() { + devServer.setLog(log); + } + + private void runAll() { + devServer.start(); + } + + private static void printUsage() { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java " + TeaVMDevServerRunner.class.getName() + " [OPTIONS] [qualified.main.Class]", + options); + System.exit(-1); + } +} diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java index 29d9e6857..1020c81da 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -20,24 +20,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.StandardWatchEventKinds; -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; @@ -46,10 +29,12 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.teavm.backend.wasm.render.WasmBinaryVersion; +import org.teavm.tooling.ConsoleTeaVMToolLog; import org.teavm.tooling.TeaVMProblemRenderer; import org.teavm.tooling.TeaVMTargetType; import org.teavm.tooling.TeaVMTool; import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.util.FileSystemWatcher; import org.teavm.vm.TeaVMOptimizationLevel; import org.teavm.vm.TeaVMPhase; import org.teavm.vm.TeaVMProgressFeedback; @@ -58,7 +43,7 @@ import org.teavm.vm.TeaVMProgressListener; public final class TeaVMRunner { private static Options options = new Options(); private TeaVMTool tool = new TeaVMTool(); - private AccumulatingTeaVMToolLog log = new AccumulatingTeaVMToolLog(new ConsoleTeaVMToolLog()); + private AccumulatingTeaVMToolLog log = new AccumulatingTeaVMToolLog(new ConsoleTeaVMToolLog(false)); private CommandLine commandLine; private long startTime; private long phaseStartTime; @@ -97,12 +82,6 @@ public final class TeaVMRunner { .hasArg() .withArgName("number") .create("O")); - options.addOption(OptionBuilder - .withArgName("separate|merge|none") - .hasArg() - .withDescription("how to attach runtime. Possible values are: separate|merge|none") - .withLongOpt("runtime") - .create("r")); options.addOption(OptionBuilder .withDescription("Generate debug information") .withLongOpt("debug") @@ -340,30 +319,28 @@ public final class TeaVMRunner { } private void buildInteractive() { - InteractiveWatcher watcher = new InteractiveWatcher(); + FileSystemWatcher watcher; + try { + watcher = new FileSystemWatcher(classPath); + } catch (IOException e) { + System.err.println("Error listening file system events"); + e.printStackTrace(); + System.exit(2); + return; + } while (true) { - ProgressListenerImpl progressListener = new ProgressListenerImpl(); - Thread thread = null; + ProgressListenerImpl progressListener = new ProgressListenerImpl(watcher); try { - watcher.progressListener = progressListener; - thread = new Thread(watcher); - thread.start(); - if (progressListener.cancelRequested.get()) { - continue; - } build(progressListener); } catch (Exception e) { e.printStackTrace(System.err); - } finally { - if (!progressListener.cancelRequested.get()) { - thread.interrupt(); - } } try { System.out.println("Waiting for changes..."); - watcher.waitForChange(); + watcher.waitForChange(750); + watcher.grabChangedFiles(); System.out.println(); System.out.println("Changes detected. Recompiling..."); } catch (InterruptedException | IOException e) { @@ -372,154 +349,9 @@ public final class TeaVMRunner { } } - class InteractiveWatcher implements Runnable { - volatile ProgressListenerImpl progressListener = new ProgressListenerImpl(); - private WatchService watchService; - private Map keysToPath = new HashMap<>(); - private Map pathsToKey = new HashMap<>(); - - InteractiveWatcher() { - try { - watchService = FileSystems.getDefault().newWatchService(); - for (String entry : classPath) { - Path path = Paths.get(entry); - File file = path.toFile(); - if (file.exists()) { - if (!file.isDirectory()) { - registerSingle(path.getParent()); - } else { - register(path); - } - } - } - } catch (IOException e) { - System.err.println("Error setting up file watcher"); - e.printStackTrace(System.err); - System.exit(2); - } - } - - private void register(Path path) throws IOException { - Files.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) - throws IOException { - registerSingle(dir); - return FileVisitResult.CONTINUE; - } - }); - } - - private void registerSingle(Path path) throws IOException { - WatchKey key = path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, - StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); - keysToPath.put(key, path); - pathsToKey.put(path, key); - } - - @Override - public void run() { - Thread thread = new Thread(() -> { - try { - waitForChange(); - if (Thread.currentThread().isInterrupted()) { - progressListener.cancelRequested.set(true); - System.out.println("Classpath changed during compilation. Cancelling..."); - } - } catch (InterruptedException | IOException e) { - // do nothing - } - }); - thread.start(); - if (thread.isAlive()) { - thread.interrupt(); - } - } - - void waitForChange() throws InterruptedException, IOException { - take(); - while (poll(750)) { - // continue polling - } - while (pollNow()) { - // continue polling - } - } - - private void take() throws InterruptedException, IOException { - while (true) { - WatchKey key = watchService.take(); - if (key != null) { - if (!filter(key).isEmpty()) { - break; - } - } - } - } - - private boolean poll(int milliseconds) throws IOException, InterruptedException { - long end = System.currentTimeMillis() + milliseconds; - while (true) { - int timeToWait = (int) (end - System.currentTimeMillis()); - WatchKey key = watchService.poll(timeToWait, TimeUnit.MILLISECONDS); - if (key == null) { - return false; - } - if (!filter(key).isEmpty()) { - break; - } - } - return true; - } - - private boolean pollNow() throws IOException { - WatchKey key = watchService.poll(); - if (key == null) { - return false; - } - filter(key); - return true; - } - - private List filter(WatchKey key) throws IOException { - List result = new ArrayList<>(); - for (WatchEvent event : key.pollEvents()) { - Path path = filter(key, event); - if (path != null) { - result.add(path); - } - } - key.reset(); - return result; - } - - private Path filter(WatchKey baseKey, WatchEvent event) throws IOException { - if (!(event.context() instanceof Path)) { - return null; - } - Path basePath = keysToPath.get(baseKey); - Path path = basePath.resolve((Path) event.context()); - WatchKey key = pathsToKey.get(path); - - if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) { - if (key != null) { - pathsToKey.remove(path); - keysToPath.remove(key); - key.cancel(); - } - } else if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) { - if (Files.isDirectory(path)) { - register(path); - } - } - - return path; - } - } - private void buildNonInteractive() { try { - build(new ProgressListenerImpl()); + build(new ProgressListenerImpl(null)); } catch (Exception e) { e.printStackTrace(System.err); System.exit(-2); @@ -561,12 +393,17 @@ public final class TeaVMRunner { class ProgressListenerImpl implements TeaVMProgressListener { private TeaVMPhase currentPhase; - AtomicBoolean cancelRequested = new AtomicBoolean(); + private FileSystemWatcher fileSystemWatcher; + + ProgressListenerImpl(FileSystemWatcher fileSystemWatcher) { + this.fileSystemWatcher = fileSystemWatcher; + } @Override public TeaVMProgressFeedback progressReached(int progress) { - return cancelRequested.get() ? TeaVMProgressFeedback.CANCEL : TeaVMProgressFeedback.CONTINUE; + return getStatus(); } + @Override public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { log.flush(); @@ -585,16 +422,25 @@ public final class TeaVMRunner { case OPTIMIZATION: System.out.print("Optimizing code..."); break; - case DECOMPILATION: - System.out.print("Decompiling..."); - break; case RENDERING: System.out.print("Generating output..."); break; } currentPhase = phase; } - return cancelRequested.get() ? TeaVMProgressFeedback.CANCEL : TeaVMProgressFeedback.CONTINUE; + return getStatus(); + } + + private TeaVMProgressFeedback getStatus() { + try { + if (fileSystemWatcher != null && fileSystemWatcher.hasChanges()) { + System.out.println("Classes changed during compilation. Canceling."); + return TeaVMProgressFeedback.CANCEL; + } + return TeaVMProgressFeedback.CONTINUE; + } catch (IOException e) { + throw new RuntimeException("IO error occurred"); + } } } diff --git a/tools/cli/src/main/java/org/teavm/cli/ConsoleTeaVMToolLog.java b/tools/core/src/main/java/org/teavm/tooling/ConsoleTeaVMToolLog.java similarity index 77% rename from tools/cli/src/main/java/org/teavm/cli/ConsoleTeaVMToolLog.java rename to tools/core/src/main/java/org/teavm/tooling/ConsoleTeaVMToolLog.java index 11b8cc374..b784b75d4 100644 --- a/tools/cli/src/main/java/org/teavm/cli/ConsoleTeaVMToolLog.java +++ b/tools/core/src/main/java/org/teavm/tooling/ConsoleTeaVMToolLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Alexey Andreev. + * Copyright 2018 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.cli; +package org.teavm.tooling; -import org.teavm.tooling.TeaVMToolLog; +public class ConsoleTeaVMToolLog implements TeaVMToolLog { + private boolean debug; + + public ConsoleTeaVMToolLog(boolean debug) { + this.debug = debug; + } -class ConsoleTeaVMToolLog implements TeaVMToolLog { @Override public void info(String text) { System.out.println("INFO: " + text); @@ -25,7 +29,9 @@ class ConsoleTeaVMToolLog implements TeaVMToolLog { @Override public void debug(String text) { - System.out.println("DEBUG: " + text); + if (debug) { + System.out.println("DEBUG: " + text); + } } @Override @@ -46,8 +52,10 @@ class ConsoleTeaVMToolLog implements TeaVMToolLog { @Override public void debug(String text, Throwable e) { - System.out.println("DEBUG: " + text); - e.printStackTrace(System.out); + if (debug) { + System.out.println("DEBUG: " + text); + e.printStackTrace(System.out); + } } @Override diff --git a/tools/core/src/main/java/org/teavm/tooling/util/FileSystemWatcher.java b/tools/core/src/main/java/org/teavm/tooling/util/FileSystemWatcher.java new file mode 100644 index 000000000..349f73d6a --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/util/FileSystemWatcher.java @@ -0,0 +1,181 @@ +/* + * Copyright 2018 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.tooling.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class FileSystemWatcher { + private WatchService watchService; + private Map keysToPath = new HashMap<>(); + private Map pathsToKey = new HashMap<>(); + private Set changedFiles = new LinkedHashSet<>(); + + public FileSystemWatcher(String[] classPath) throws IOException { + watchService = FileSystems.getDefault().newWatchService(); + for (String entry : classPath) { + Path path = Paths.get(entry); + File file = path.toFile(); + if (file.exists()) { + if (!file.isDirectory()) { + registerSingle(path.getParent()); + } else { + register(path); + } + } + } + } + + public void dispose() throws IOException { + watchService.close(); + } + + private List register(Path path) throws IOException { + List files = new ArrayList<>(); + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + registerSingle(dir); + files.addAll(Arrays.stream(dir.toFile().listFiles(File::isFile)).map(File::toPath) + .collect(Collectors.toList())); + return FileVisitResult.CONTINUE; + } + }); + return files; + } + + private void registerSingle(Path path) throws IOException { + WatchKey key = path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); + keysToPath.put(key, path); + pathsToKey.put(path, key); + } + + public boolean hasChanges() throws IOException { + return !changedFiles.isEmpty() || pollNow(); + } + + public void waitForChange(int timeout) throws InterruptedException, IOException { + if (!hasChanges()) { + take(); + } + while (poll(timeout)) { + // continue polling + } + while (pollNow()) { + // continue polling + } + } + + public List grabChangedFiles() { + List result = new ArrayList<>(changedFiles); + changedFiles.clear(); + return result; + } + + private void take() throws InterruptedException, IOException { + while (true) { + WatchKey key = watchService.take(); + if (key != null && filter(key)) { + return; + } + } + } + + private boolean poll(int milliseconds) throws IOException, InterruptedException { + long end = System.currentTimeMillis() + milliseconds; + while (true) { + int timeToWait = (int) (end - System.currentTimeMillis()); + if (timeToWait <= 0) { + return false; + } + WatchKey key = watchService.poll(timeToWait, TimeUnit.MILLISECONDS); + if (key == null) { + continue; + } + if (filter(key)) { + return true; + } + } + } + + private boolean pollNow() throws IOException { + WatchKey key = watchService.poll(); + if (key == null) { + return false; + } + return filter(key); + } + + private boolean filter(WatchKey key) throws IOException { + boolean hasNew = false; + for (WatchEvent event : key.pollEvents()) { + List paths = filter(key, event); + if (!paths.isEmpty()) { + changedFiles.addAll(paths.stream().map(Path::toFile).collect(Collectors.toList())); + hasNew = true; + } + } + key.reset(); + return hasNew; + } + + private List filter(WatchKey baseKey, WatchEvent event) throws IOException { + if (!(event.context() instanceof Path)) { + return Collections.emptyList(); + } + Path basePath = keysToPath.get(baseKey); + Path path = basePath.resolve((Path) event.context()); + WatchKey key = pathsToKey.get(path); + + List result = new ArrayList<>(); + result.add(path); + + if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) { + if (key != null) { + pathsToKey.remove(path); + keysToPath.remove(key); + key.cancel(); + } + } else if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) { + if (Files.isDirectory(path)) { + result.addAll(register(path)); + } + } + return result; + } +} diff --git a/tools/devserver/pom.xml b/tools/devserver/pom.xml new file mode 100644 index 000000000..0bcc64922 --- /dev/null +++ b/tools/devserver/pom.xml @@ -0,0 +1,129 @@ + + + 4.0.0 + + + org.teavm + teavm + 0.6.0-SNAPSHOT + ../.. + + teavm-devserver + + TeaVM Dev Sever + TeaVM development server that builds and serves JavaScript files + + + + commons-io + commons-io + true + + + org.ow2.asm + asm-commons + true + + + org.ow2.asm + asm-util + true + + + com.carrotsearch + hppc + 0.7.3 + true + + + org.mozilla + rhino + true + + + + org.teavm + teavm-core + ${project.version} + + + org.teavm + teavm-tooling + ${project.version} + + + org.teavm + teavm-classlib + ${project.version} + runtime + + + org.teavm + teavm-metaprogramming-impl + ${project.version} + runtime + + + org.teavm + teavm-jso-impl + ${project.version} + runtime + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty.websocket + javax-websocket-server-impl + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + com.fasterxml.jackson.core + jackson-databind + true + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ../../checkstyle.xml + config_loc=${basedir}/../.. + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + \ No newline at end of file diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java new file mode 100644 index 000000000..0b788e5a2 --- /dev/null +++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java @@ -0,0 +1,566 @@ +/* + * Copyright 2018 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.devserver; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.teavm.backend.javascript.JavaScriptTarget; +import org.teavm.cache.InMemoryMethodNodeCache; +import org.teavm.cache.InMemoryProgramCache; +import org.teavm.cache.MemoryCachedClassReaderSource; +import org.teavm.debugging.information.DebugInformation; +import org.teavm.debugging.information.DebugInformationBuilder; +import org.teavm.dependency.FastDependencyAnalyzer; +import org.teavm.model.ClassReader; +import org.teavm.model.PreOptimizingClassHolderSource; +import org.teavm.parsing.ClasspathClassHolderSource; +import org.teavm.tooling.EmptyTeaVMToolLog; +import org.teavm.tooling.TeaVMProblemRenderer; +import org.teavm.tooling.TeaVMToolLog; +import org.teavm.tooling.util.FileSystemWatcher; +import org.teavm.vm.MemoryBuildTarget; +import org.teavm.vm.TeaVM; +import org.teavm.vm.TeaVMBuilder; +import org.teavm.vm.TeaVMOptimizationLevel; +import org.teavm.vm.TeaVMPhase; +import org.teavm.vm.TeaVMProgressFeedback; +import org.teavm.vm.TeaVMProgressListener; + +public class CodeServlet extends HttpServlet { + private static final Supplier EMPTY_CONTENT = () -> null; + + private String mainClass; + private String[] classPath; + private String fileName = "classes.js"; + private String pathToFile = "/"; + private List sourcePath = new ArrayList<>(); + private TeaVMToolLog log = new EmptyTeaVMToolLog(); + private boolean indicator; + private int port; + + private Map> sourceFileCache = new HashMap<>(); + + private volatile boolean stopped; + private FileSystemWatcher watcher; + private MemoryCachedClassReaderSource classSource; + private InMemoryProgramCache programCache; + private InMemoryMethodNodeCache astCache; + private int lastReachedClasses; + private boolean firstTime = true; + + private final Object contentLock = new Object(); + private final Map content = new HashMap<>(); + private MemoryBuildTarget buildTarget = new MemoryBuildTarget(); + + private CodeWsEndpoint wsEndpoint; + + public CodeServlet(String mainClass, String[] classPath) { + this.mainClass = mainClass; + this.classPath = classPath.clone(); + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public void setPathToFile(String pathToFile) { + if (!pathToFile.endsWith("/")) { + pathToFile += "/"; + } + if (!pathToFile.startsWith("/")) { + pathToFile = "/" + pathToFile; + } + this.pathToFile = pathToFile; + } + + public List getSourcePath() { + return sourcePath; + } + + public void setLog(TeaVMToolLog log) { + this.log = log; + } + + public void setIndicator(boolean indicator) { + this.indicator = indicator; + } + + public void setPort(int port) { + this.port = port; + } + + public void setWsEndpoint(CodeWsEndpoint wsEndpoint) { + this.wsEndpoint = wsEndpoint; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String path = req.getPathInfo(); + if (path != null) { + log.debug("Serving " + path); + if (!path.startsWith("/")) { + path = "/" + path; + } + if (path.startsWith(pathToFile) && path.length() > pathToFile.length()) { + String fileName = path.substring(pathToFile.length()); + if (fileName.startsWith("src/")) { + if (serveSourceFile(fileName.substring("src/".length()), resp)) { + log.debug("File " + path + " served as source file"); + return; + } + } else { + byte[] fileContent; + boolean firstTime; + synchronized (contentLock) { + fileContent = content.get(fileName); + firstTime = this.firstTime; + } + if (fileContent != null) { + resp.setStatus(HttpServletResponse.SC_OK); + resp.setCharacterEncoding("UTF-8"); + resp.setContentType("text/plain"); + resp.getOutputStream().write(fileContent); + resp.getOutputStream().flush(); + log.debug("File " + path + " served as generated file"); + return; + } else if (fileName.equals(this.fileName) && indicator && firstTime) { + serveBootFile(resp); + return; + } + } + } + } + + log.debug("File " + path + " not found"); + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + + @Override + public void destroy() { + super.destroy(); + stopped = true; + } + + @Override + public void init() throws ServletException { + super.init(); + Thread thread = new Thread(this::runTeaVM); + thread.setDaemon(true); + thread.setName("TeaVM compiler"); + thread.start(); + } + + private boolean serveSourceFile(String fileName, HttpServletResponse resp) throws IOException { + try (InputStream stream = sourceFileCache.computeIfAbsent(fileName, this::findSourceFile).get()) { + if (stream == null) { + return false; + } + + resp.setStatus(HttpServletResponse.SC_OK); + resp.setCharacterEncoding("UTF-8"); + resp.setContentType("text/plain"); + IOUtils.copy(stream, resp.getOutputStream()); + resp.getOutputStream().flush(); + return true; + } + } + + private Supplier findSourceFile(String fileName) { + for (String element : sourcePath) { + File sourceFile = new File(element); + if (sourceFile.isFile()) { + Supplier result = findSourceFileInZip(sourceFile, fileName); + if (result != null) { + return result; + } + } else if (sourceFile.isDirectory()) { + File result = new File(sourceFile, fileName); + if (result.exists()) { + return () -> { + try { + return new FileInputStream(result); + } catch (FileNotFoundException e) { + return null; + } + }; + } + } + } + + return EMPTY_CONTENT; + } + + private Supplier findSourceFileInZip(File zipFile, String fileName) { + try (ZipFile zip = new ZipFile(zipFile)) { + ZipEntry entry = zip.getEntry(fileName); + if (entry == null) { + return null; + } + return () -> { + try { + ZipInputStream input = new ZipInputStream(new FileInputStream(zipFile)); + while (true) { + ZipEntry e = input.getNextEntry(); + if (e == null) { + return null; + } + if (e.getName().equals(fileName)) { + return input; + } + } + } catch (IOException e) { + return null; + } + }; + } catch (IOException e) { + return null; + } + } + + private void serveBootFile(HttpServletResponse resp) throws IOException { + resp.setStatus(HttpServletResponse.SC_OK); + resp.setCharacterEncoding("UTF-8"); + resp.setContentType("text/plain"); + resp.getWriter().write("function main() { }\n"); + resp.getWriter().write(getIndicatorScript(true)); + resp.getWriter().flush(); + log.debug("Served boot file"); + } + + private void runTeaVM() { + try { + initBuilder(); + + while (!stopped) { + buildOnce(); + + if (stopped) { + break; + } + + try { + watcher.waitForChange(750); + } catch (InterruptedException e) { + log.info("Build thread interrupted"); + break; + } + log.info("Changes detected. Recompiling."); + List staleClasses = getChangedClasses(watcher.grabChangedFiles()); + log.debug("Following classes changed: " + staleClasses); + classSource.evict(staleClasses); + } + } catch (Throwable e) { + log.error("Compile server crashed", e); + } finally { + shutdownBuilder(); + } + } + + private void initBuilder() throws IOException { + watcher = new FileSystemWatcher(classPath); + + classSource = new MemoryCachedClassReaderSource(); + astCache = new InMemoryMethodNodeCache(); + programCache = new InMemoryProgramCache(); + } + + private void shutdownBuilder() { + try { + watcher.dispose(); + } catch (IOException e) { + log.debug("Exception caught", e); + } + classSource = null; + watcher = null; + astCache = null; + programCache = null; + synchronized (content) { + content.clear(); + } + buildTarget.clear(); + + log.info("Build thread complete"); + } + + private void buildOnce() { + DebugInformationBuilder debugInformationBuilder = new DebugInformationBuilder(); + ClassLoader classLoader = initClassLoader(); + classSource.setUnderlyingSource(new PreOptimizingClassHolderSource( + new ClasspathClassHolderSource(classLoader))); + + long startTime = System.currentTimeMillis(); + JavaScriptTarget jsTarget = new JavaScriptTarget(); + + TeaVM vm = new TeaVMBuilder(jsTarget) + .setClassLoader(classLoader) + .setClassSource(classSource) + .setDependencyAnalyzerFactory(FastDependencyAnalyzer::new) + .build(); + + jsTarget.setStackTraceIncluded(true); + jsTarget.setMinifying(false); + jsTarget.setAstCache(astCache); + jsTarget.setDebugEmitter(debugInformationBuilder); + vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); + vm.setCacheStatus(classSource); + vm.addVirtualMethods(m -> true); + vm.setProgressListener(progressListener); + vm.setProgramCache(programCache); + vm.installPlugins(); + + vm.setLastKnownClasses(lastReachedClasses); + vm.entryPoint(mainClass); + + log.info("Starting build"); + progressListener.last = 0; + progressListener.lastTime = System.currentTimeMillis(); + vm.build(buildTarget, fileName); + addIndicator(); + generateDebug(debugInformationBuilder); + + postBuild(vm, startTime); + } + + private void addIndicator() { + if (!indicator) { + return; + } + + String script = getIndicatorScript(false); + try (Writer writer = new OutputStreamWriter(buildTarget.appendToResource(fileName), StandardCharsets.UTF_8)) { + writer.append("\n"); + writer.append(script); + } catch (IOException e) { + throw new RuntimeException("IO error occurred writing debug information", e); + } + } + + private String getIndicatorScript(boolean boot) { + try (Reader reader = new InputStreamReader(CodeServlet.class.getResourceAsStream("indicator.js"), + StandardCharsets.UTF_8)) { + String script = IOUtils.toString(reader); + script = script.substring(script.indexOf("*/") + 2); + script = script.replace("WS_PATH", "localhost:" + port + pathToFile + fileName + ".ws"); + script = script.replace("BOOT_FLAG", Boolean.toString(boot)); + script = script.replace("FILE_NAME", "http://localhost:" + port + pathToFile + fileName); + return script; + } catch (IOException e) { + throw new RuntimeException("IO error occurred writing debug information", e); + } + } + + private void generateDebug(DebugInformationBuilder debugInformationBuilder) { + try { + DebugInformation debugInformation = debugInformationBuilder.getDebugInformation(); + String sourceMapName = fileName + ".map"; + + try (Writer writer = new OutputStreamWriter(buildTarget.appendToResource(fileName), + StandardCharsets.UTF_8)) { + writer.append("\n//# sourceMappingURL=" + sourceMapName); + } + + try (Writer writer = new OutputStreamWriter(buildTarget.createResource(sourceMapName), + StandardCharsets.UTF_8)) { + debugInformation.writeAsSourceMaps(writer, "src", fileName); + } + debugInformation.write(buildTarget.createResource(fileName + ".teavmdbg")); + } catch (IOException e) { + throw new RuntimeException("IO error occurred writing debug information", e); + } + } + + private void postBuild(TeaVM vm, long startTime) { + if (!vm.wasCancelled()) { + if (vm.getProblemProvider().getSevereProblems().isEmpty()) { + log.info("Build complete successfully"); + saveNewResult(); + lastReachedClasses = vm.getDependencyInfo().getReachableClasses().size(); + classSource.commit(); + if (wsEndpoint != null) { + wsEndpoint.complete(true); + } + } else { + log.info("Build complete with errors"); + if (wsEndpoint != null) { + wsEndpoint.complete(false); + } + } + printStats(vm, startTime); + TeaVMProblemRenderer.describeProblems(vm, log); + } else { + log.info("Build cancelled"); + } + + buildTarget.clear(); + } + + private void printStats(TeaVM vm, long startTime) { + if (vm.getWrittenClasses() != null) { + int classCount = vm.getWrittenClasses().getClassNames().size(); + int methodCount = 0; + for (String className : vm.getWrittenClasses().getClassNames()) { + ClassReader cls = vm.getWrittenClasses().get(className); + methodCount += cls.getMethods().size(); + } + + log.info("Classes compiled: " + classCount); + log.info("Methods compiled: " + methodCount); + } + + log.info("Compilation took " + (System.currentTimeMillis() - startTime) + " ms"); + } + + private void saveNewResult() { + synchronized (contentLock) { + firstTime = false; + content.clear(); + for (String name : buildTarget.getNames()) { + content.put(name, buildTarget.getContent(name)); + } + } + } + + private List getChangedClasses(Collection changedFiles) { + List result = new ArrayList<>(); + + for (File file : changedFiles) { + String path = file.getPath(); + if (!path.endsWith(".class")) { + continue; + } + + String prefix = Arrays.stream(classPath) + .filter(path::startsWith) + .findFirst() + .orElse(""); + int start = prefix.length(); + if (start < path.length() && path.charAt(start) == '/') { + ++start; + } + + path = path.substring(start, path.length() - ".class".length()).replace('/', '.'); + result.add(path); + } + + return result; + } + + private ClassLoader initClassLoader() { + URL[] urls = new URL[classPath.length]; + try { + for (int i = 0; i < classPath.length; i++) { + urls[i] = new File(classPath[i]).toURI().toURL(); + } + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + return new URLClassLoader(urls, CodeServlet.class.getClassLoader()); + } + + private final ProgressListenerImpl progressListener = new ProgressListenerImpl(); + + class ProgressListenerImpl implements TeaVMProgressListener { + private int start; + private int end; + private int phaseLimit; + private int last; + private long lastTime; + + @Override + public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { + switch (phase) { + case DEPENDENCY_ANALYSIS: + start = 0; + end = 500; + break; + case LINKING: + start = 400; + end = 500; + break; + case OPTIMIZATION: + start = 500; + end = 750; + break; + case RENDERING: + start = 750; + end = 1000; + break; + } + phaseLimit = count; + return progressReached(0); + } + + @Override + public TeaVMProgressFeedback progressReached(int progress) { + if (wsEndpoint != null && indicator) { + int current = start + Math.min(progress, phaseLimit) * (end - start) / phaseLimit; + if (current != last) { + if (current - last > 10 || System.currentTimeMillis() - lastTime > 100) { + lastTime = System.currentTimeMillis(); + last = current; + wsEndpoint.progress(current / 10.0); + } + } + } + return getResult(); + } + + private TeaVMProgressFeedback getResult() { + if (stopped) { + log.info("Trying to cancel compilation due to server stopping"); + return TeaVMProgressFeedback.CANCEL; + } + + try { + if (watcher.hasChanges()) { + log.info("Changes detected, cancelling build"); + return TeaVMProgressFeedback.CANCEL; + } + } catch (IOException e) { + log.info("IO error occurred", e); + return TeaVMProgressFeedback.CANCEL; + } + + return TeaVMProgressFeedback.CONTINUE; + } + } +} diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeWsEndpoint.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeWsEndpoint.java new file mode 100644 index 000000000..9d2382dea --- /dev/null +++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeWsEndpoint.java @@ -0,0 +1,44 @@ +/* + * Copyright 2018 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.devserver; + +import java.util.function.Consumer; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/") +public class CodeWsEndpoint { + private Session session; + + @OnOpen + public void open(Session session) { + this.session = session; + @SuppressWarnings("unchecked") + Consumer consumer = (Consumer) session.getUserProperties().get("ws.consumer"); + if (consumer != null) { + consumer.accept(this); + } + } + + public void progress(double value) { + session.getAsyncRemote().sendText("{ \"command\": \"compiling\", \"progress\": " + value + " }"); + } + + public void complete(boolean success) { + session.getAsyncRemote().sendText("{ \"command\": \"complete\", \"success\": " + success + " }"); + } +} diff --git a/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java b/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java new file mode 100644 index 000000000..507c00d1b --- /dev/null +++ b/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java @@ -0,0 +1,170 @@ +/* + * Copyright 2018 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.devserver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import javax.websocket.Decoder; +import javax.websocket.Encoder; +import javax.websocket.Extension; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpointConfig; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; +import org.teavm.tooling.ConsoleTeaVMToolLog; +import org.teavm.tooling.TeaVMToolLog; + +public class DevServer { + private String mainClass; + private String[] classPath; + private String pathToFile = ""; + private String fileName = "classes.js"; + private List sourcePath = new ArrayList<>(); + private boolean indicator; + private TeaVMToolLog log = new ConsoleTeaVMToolLog(false); + + private Server server; + private int port = 9090; + + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + } + + public void setClassPath(String[] classPath) { + this.classPath = classPath; + } + + public void setPort(int port) { + this.port = port; + } + + public void setPathToFile(String pathToFile) { + if (!pathToFile.startsWith("/")) { + pathToFile = "/" + pathToFile; + } + if (!pathToFile.endsWith("/")) { + pathToFile += "/"; + } + this.pathToFile = pathToFile; + } + + public void setLog(TeaVMToolLog log) { + this.log = log; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public void setIndicator(boolean indicator) { + this.indicator = indicator; + } + + public List getSourcePath() { + return sourcePath; + } + + public void start() { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(port); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + CodeServlet servlet = new CodeServlet(mainClass, classPath); + servlet.setFileName(fileName); + servlet.setPathToFile(pathToFile); + servlet.setLog(log); + servlet.getSourcePath().addAll(sourcePath); + servlet.setIndicator(indicator); + servlet.setPort(port); + context.addServlet(new ServletHolder(servlet), "/*"); + + try { + ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); + wscontainer.addEndpoint(new DevServerEndpointConfig(servlet::setWsEndpoint)); + server.start(); + server.join(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private class DevServerEndpointConfig implements ServerEndpointConfig { + private Map userProperties = new HashMap<>(); + + public DevServerEndpointConfig(Consumer consumer) { + userProperties.put("ws.consumer", consumer); + } + + @Override + public List> getDecoders() { + return Collections.emptyList(); + } + + @Override + public List> getEncoders() { + return Collections.emptyList(); + } + + @Override + public Map getUserProperties() { + return userProperties; + } + + @Override + public Configurator getConfigurator() { + return null; + } + + @Override + public Class getEndpointClass() { + return CodeWsEndpoint.class; + } + + @Override + public List getExtensions() { + return Collections.emptyList(); + } + + @Override + public String getPath() { + return pathToFile + fileName + ".ws"; + } + + @Override + public List getSubprotocols() { + return Collections.emptyList(); + } + } +} diff --git a/tools/devserver/src/main/resources/org/teavm/devserver/indicator.js b/tools/devserver/src/main/resources/org/teavm/devserver/indicator.js new file mode 100644 index 000000000..3e191a329 --- /dev/null +++ b/tools/devserver/src/main/resources/org/teavm/devserver/indicator.js @@ -0,0 +1,153 @@ +/* + * Copyright 2018 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. + */ +(function () { + var boot = BOOT_FLAG; + + function createWebSocket() { + var loc = window.location; + var newUri = loc.protocol === "https:" ? "wss:" : "ws:"; + newUri += "//WS_PATH"; + return new WebSocket(newUri); + } + + function createIndicator() { + function createMainElement() { + var element = document.createElement("div"); + element.style.position = "fixed"; + element.style.left = "0"; + element.style.bottom = "0"; + element.style.backgroundColor = "black"; + element.style.color = "white"; + element.style.opacity = 0.4; + element.style.padding = "5px"; + element.style.fontSize = "18px"; + element.style.fontWeight = "bold"; + element.style.pointerEvents = "none"; + element.style.zIndex = 1000; + element.style.display = "none"; + return element; + } + + function createLabelElement() { + return document.createElement("span"); + } + + function createProgressElements() { + var element = document.createElement("span"); + element.style.display = "none"; + element.style.marginLeft = "10px"; + element.style.width = "150px"; + element.style.height = "15px"; + element.style.borderWidth = "1px"; + element.style.borderColor = "rgb(170,170,170)"; + element.style.borderStyle = "solid"; + element.style.borderRadius = "2px"; + element.style.backgroundColor = "white"; + element.style.position = "relative"; + + var progress = document.createElement("span"); + progress.style.display = "block"; + progress.style.position = "absolute"; + progress.style.left = "0"; + progress.style.top = "0"; + progress.style.bottom = "0"; + progress.style.backgroundColor = "rgb(210,210,210)"; + progress.style.borderWidth = "1px"; + progress.style.borderColor = "rgb(170,170,170)"; + progress.style.borderRightStyle = "solid"; + + element.appendChild(progress); + + return { + container: element, + indicator: progress + }; + } + + var container = createMainElement(); + var label = createLabelElement(); + var progress = createProgressElements(); + container.appendChild(label); + container.appendChild(progress.container); + + return { + container: container, + label: label, + progress: progress, + timer: void 0, + + show: function(text, timeout) { + this.container.style.display = ""; + this.label.innerText = text; + if (this.timer) { + clearTimeout(this.timer); + this.timer = void 0; + } + if (timeout) { + setTimeout(function() { + this.timer = void 0; + this.container.style.display = "none"; + }.bind(this), timeout * 1000); + } + }, + + showProgress(value) { + this.progress.container.style.display = "inline-block"; + this.progress.indicator.style.width = value.toFixed(2) + "%"; + }, + + hideProgress() { + this.progress.container.style.display = "none"; + } + }; + } + + var indicator = createIndicator(); + window.addEventListener("load", function() { + document.body.appendChild(indicator.container); + }); + + var ws = createWebSocket(); + ws.onmessage = function(event) { + var message = JSON.parse(event.data); + switch (message.command) { + case "compiling": + indicator.show("Compiling..."); + indicator.showProgress(message.progress || 0); + break; + case "complete": + if (message.success) { + indicator.show("Compilation complete", 10); + if (boot) { + var scriptElem = document.createElement("script"); + scriptElem.src = "FILE_NAME"; + scriptElem.onload = function() { + main(); + }; + document.head.appendChild(scriptElem); + } + } else { + indicator.show("Compilation errors occurred") + } + indicator.hideProgress(); + break; + } + }; + + if (boot) { + indicator.show("File is not ready, about to compile"); + } +})(); \ No newline at end of file diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java index 1950e3425..5b83d7ea1 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -182,8 +182,6 @@ class TeaVMBuild { return "Discovering classes to compile"; case LINKING: return "Resolving method invocations"; - case DECOMPILATION: - return "Compiling classes"; case OPTIMIZATION: return "Optimizing code"; case RENDERING: