mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Implement development server
This commit is contained in:
parent
952ed3f193
commit
eec458089f
|
@ -324,6 +324,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, naming, sourceWriter);
|
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, naming, sourceWriter);
|
||||||
renderer.setProperties(controller.getProperties());
|
renderer.setProperties(controller.getProperties());
|
||||||
renderer.setMinifying(minifying);
|
renderer.setMinifying(minifying);
|
||||||
|
renderer.setProgressConsumer(controller::reportProgress);
|
||||||
if (debugEmitter != null) {
|
if (debugEmitter != null) {
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
ClassHolder cls = classes.get(className);
|
ClassHolder cls = classes.get(className);
|
||||||
|
@ -352,7 +353,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
|
|
||||||
renderer.prepare(clsNodes);
|
renderer.prepare(clsNodes);
|
||||||
runtimeRenderer.renderRuntime();
|
runtimeRenderer.renderRuntime();
|
||||||
renderer.render(clsNodes);
|
if (!renderer.render(clsNodes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
renderer.renderStringPool();
|
renderer.renderStringPool();
|
||||||
renderer.renderStringConstants();
|
renderer.renderStringConstants();
|
||||||
renderer.renderCompatibilityStubs();
|
renderer.renderCompatibilityStubs();
|
||||||
|
|
|
@ -52,20 +52,20 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFullNameFor(MethodReference method) throws NamingException {
|
public String getFullNameFor(MethodReference method) {
|
||||||
return getFullNameFor(method, 'M');
|
return getFullNameFor(method, 'M');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNameForInit(MethodReference method) throws NamingException {
|
public String getNameForInit(MethodReference method) {
|
||||||
return getFullNameFor(method, 'I');
|
return getFullNameFor(method, 'I');
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFullNameFor(MethodReference method, char classifier) throws NamingException {
|
private String getFullNameFor(MethodReference method, char classifier) {
|
||||||
MethodReference originalMethod = method;
|
MethodReference originalMethod = method;
|
||||||
method = getRealMethod(method);
|
method = getRealMethod(method);
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NamingException("Can't provide name for method as it was not found: " + originalMethod);
|
method = originalMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodReference resolvedMethod = method;
|
MethodReference resolvedMethod = method;
|
||||||
|
@ -86,7 +86,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFullNameFor(FieldReference field) throws NamingException {
|
public String getFullNameFor(FieldReference field) {
|
||||||
String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName());
|
String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName());
|
||||||
if (!realCls.equals(field.getClassName())) {
|
if (!realCls.equals(field.getClassName())) {
|
||||||
String alias = getNameFor(new FieldReference(realCls, field.getFieldName()));
|
String alias = getNameFor(new FieldReference(realCls, field.getFieldName()));
|
||||||
|
@ -99,12 +99,12 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNameForFunction(String name) throws NamingException {
|
public String getNameForFunction(String name) {
|
||||||
return functionAliases.computeIfAbsent(name, key -> aliasProvider.getFunctionAlias(key));
|
return functionAliases.computeIfAbsent(name, key -> aliasProvider.getFunctionAlias(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNameForClassInit(String className) throws NamingException {
|
public String getNameForClassInit(String className) {
|
||||||
return classInitAliases.computeIfAbsent(className, key -> aliasProvider.getClassInitAlias(key));
|
return classInitAliases.computeIfAbsent(className, key -> aliasProvider.getClassInitAlias(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,17 +131,16 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
String initialCls = cls;
|
String initialCls = cls;
|
||||||
while (!fieldExists(cls, field)) {
|
while (!fieldExists(cls, field)) {
|
||||||
ClassReader clsHolder = classSource.get(cls);
|
ClassReader clsHolder = classSource.get(cls);
|
||||||
cls = clsHolder.getParent();
|
if (clsHolder == null || clsHolder.getParent() == null) {
|
||||||
if (cls == null) {
|
return initialCls;
|
||||||
throw new NamingException("Can't provide name for field as the field not found: "
|
|
||||||
+ initialCls + "." + field);
|
|
||||||
}
|
}
|
||||||
|
cls = clsHolder.getParent();
|
||||||
}
|
}
|
||||||
return cls;
|
return cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean fieldExists(String cls, String field) {
|
private boolean fieldExists(String cls, String field) {
|
||||||
ClassReader classHolder = classSource.get(cls);
|
ClassReader classHolder = classSource.get(cls);
|
||||||
return classHolder.getField(field) != null;
|
return classHolder != null && classHolder.getField(field) != 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,19 +20,19 @@ import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public interface NamingStrategy {
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,45 +98,43 @@ public class SourceWriter implements Appendable, LocationProvider {
|
||||||
innerWriter.append(csq, start, end);
|
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));
|
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()));
|
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));
|
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));
|
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));
|
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)));
|
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));
|
return append(naming.getFullNameFor(method));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceWriter appendMethodBody(String className, String name, ValueType... params)
|
public SourceWriter appendMethodBody(String className, String name, ValueType... params) throws IOException {
|
||||||
throws NamingException, IOException {
|
|
||||||
return append(naming.getFullNameFor(new MethodReference(className, new MethodDescriptor(name, params))));
|
return append(naming.getFullNameFor(new MethodReference(className, new MethodDescriptor(name, params))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceWriter appendMethodBody(Class<?> cls, String name, Class<?>... params)
|
public SourceWriter appendMethodBody(Class<?> cls, String name, Class<?>... params) throws IOException {
|
||||||
throws NamingException, IOException {
|
|
||||||
return append(naming.getFullNameFor(new MethodReference(cls, name, params)));
|
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));
|
return append(naming.getNameForFunction(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.teavm.ast.AsyncMethodNode;
|
import org.teavm.ast.AsyncMethodNode;
|
||||||
import org.teavm.ast.AsyncMethodPart;
|
import org.teavm.ast.AsyncMethodPart;
|
||||||
|
@ -37,7 +38,6 @@ import org.teavm.ast.MethodNodeVisitor;
|
||||||
import org.teavm.ast.NativeMethodNode;
|
import org.teavm.ast.NativeMethodNode;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.ast.VariableNode;
|
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.NamingOrderer;
|
||||||
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
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.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.vm.RenderingException;
|
import org.teavm.vm.RenderingException;
|
||||||
|
import org.teavm.vm.TeaVMProgressFeedback;
|
||||||
|
|
||||||
public class Renderer implements RenderingManager {
|
public class Renderer implements RenderingManager {
|
||||||
private final NamingStrategy naming;
|
private final NamingStrategy naming;
|
||||||
|
@ -72,6 +73,7 @@ public class Renderer implements RenderingManager {
|
||||||
private final Diagnostics diagnostics;
|
private final Diagnostics diagnostics;
|
||||||
private RenderingContext context;
|
private RenderingContext context;
|
||||||
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
||||||
|
private IntFunction<TeaVMProgressFeedback> progressConsumer = p -> TeaVMProgressFeedback.CONTINUE;
|
||||||
|
|
||||||
private ObjectIntMap<String> sizeByClass = new ObjectIntHashMap<>();
|
private ObjectIntMap<String> sizeByClass = new ObjectIntHashMap<>();
|
||||||
private int stringPoolSize;
|
private int stringPoolSize;
|
||||||
|
@ -161,6 +163,10 @@ public class Renderer implements RenderingManager {
|
||||||
this.debugEmitter = debugEmitter;
|
this.debugEmitter = debugEmitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProgressConsumer(IntFunction<TeaVMProgressFeedback> progressConsumer) {
|
||||||
|
this.progressConsumer = progressConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
public void setProperties(Properties properties) {
|
public void setProperties(Properties properties) {
|
||||||
this.properties.clear();
|
this.properties.clear();
|
||||||
this.properties.putAll(properties);
|
this.properties.putAll(properties);
|
||||||
|
@ -265,7 +271,7 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(List<ClassNode> classes) throws RenderingException {
|
public boolean render(List<ClassNode> classes) throws RenderingException {
|
||||||
if (minifying) {
|
if (minifying) {
|
||||||
try {
|
try {
|
||||||
renderRuntimeAliases();
|
renderRuntimeAliases();
|
||||||
|
@ -273,13 +279,18 @@ public class Renderer implements RenderingManager {
|
||||||
throw new RenderingException(e);
|
throw new RenderingException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int index = 0;
|
||||||
for (ClassNode cls : classes) {
|
for (ClassNode cls : classes) {
|
||||||
int start = writer.getOffset();
|
int start = writer.getOffset();
|
||||||
renderDeclaration(cls);
|
renderDeclaration(cls);
|
||||||
renderMethodBodies(cls);
|
renderMethodBodies(cls);
|
||||||
appendClassSize(cls.getName(), writer.getOffset() - start);
|
appendClassSize(cls.getName(), writer.getOffset() - start);
|
||||||
|
if (progressConsumer.apply(1000 * ++index / classes.size()) == TeaVMProgressFeedback.CANCEL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
renderClassMetadata(classes);
|
renderClassMetadata(classes);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderDeclaration(ClassNode cls) throws RenderingException {
|
private void renderDeclaration(ClassNode cls) throws RenderingException {
|
||||||
|
@ -337,8 +348,6 @@ public class Renderer implements RenderingManager {
|
||||||
writer.append("var ").appendStaticField(fieldRef).ws().append("=").ws()
|
writer.append("var ").appendStaticField(fieldRef).ws().append("=").ws()
|
||||||
.append(context.constantToString(value)).append(";").softNewLine();
|
.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) {
|
} catch (IOException e) {
|
||||||
throw new RenderingException("IO error occurred", e);
|
throw new RenderingException("IO error occurred", e);
|
||||||
}
|
}
|
||||||
|
@ -366,8 +375,6 @@ public class Renderer implements RenderingManager {
|
||||||
for (MethodNode method : cls.getMethods()) {
|
for (MethodNode method : cls.getMethods()) {
|
||||||
renderBody(method);
|
renderBody(method);
|
||||||
}
|
}
|
||||||
} catch (NamingException e) {
|
|
||||||
throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RenderingException("IO error occurred", e);
|
throw new RenderingException("IO error occurred", e);
|
||||||
}
|
}
|
||||||
|
@ -508,8 +515,6 @@ public class Renderer implements RenderingManager {
|
||||||
renderVirtualDeclarations(virtualMethods);
|
renderVirtualDeclarations(virtualMethods);
|
||||||
}
|
}
|
||||||
writer.append("]);").newLine();
|
writer.append("]);").newLine();
|
||||||
} catch (NamingException e) {
|
|
||||||
throw new RenderingException("Error rendering class metadata. See a cause for details", e);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RenderingException("IO error occurred", e);
|
throw new RenderingException("IO error occurred", e);
|
||||||
}
|
}
|
||||||
|
@ -688,7 +693,7 @@ public class Renderer implements RenderingManager {
|
||||||
return minifying ? RenderingUtil.indexToId(index) : "var_" + index;
|
return minifying ? RenderingUtil.indexToId(index) : "var_" + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderVirtualDeclarations(Collection<MethodReference> methods) throws NamingException, IOException {
|
private void renderVirtualDeclarations(Collection<MethodReference> methods) throws IOException {
|
||||||
if (methods.stream().noneMatch(this::isVirtual)) {
|
if (methods.stream().noneMatch(this::isVirtual)) {
|
||||||
writer.append('0');
|
writer.append('0');
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets;
|
||||||
import org.mozilla.javascript.CompilerEnvirons;
|
import org.mozilla.javascript.CompilerEnvirons;
|
||||||
import org.mozilla.javascript.Context;
|
import org.mozilla.javascript.Context;
|
||||||
import org.mozilla.javascript.ast.AstRoot;
|
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.NamingStrategy;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
@ -73,8 +72,6 @@ public class RuntimeRenderer {
|
||||||
renderRuntimeCreateException();
|
renderRuntimeCreateException();
|
||||||
renderCreateStackTraceElement();
|
renderCreateStackTraceElement();
|
||||||
renderSetStackTrace();
|
renderSetStackTrace();
|
||||||
} catch (NamingException e) {
|
|
||||||
throw new RenderingException("Error rendering runtime methods. See a cause for details", e);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RenderingException("IO error", e);
|
throw new RenderingException("IO error", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
65
core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java
vendored
Normal file
65
core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java
vendored
Normal file
|
@ -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<String, Optional<ClassReader>> cache = new HashMap<>();
|
||||||
|
private final Set<String> 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<? extends String> classes) {
|
||||||
|
cache.keySet().removeAll(classes);
|
||||||
|
freshClasses.removeAll(classes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -648,7 +648,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
if (interrupted) {
|
if (interrupted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int index = 0;
|
|
||||||
while (!deferredTasks.isEmpty() || !tasks.isEmpty() || !pendingTransitions.isEmpty()) {
|
while (!deferredTasks.isEmpty() || !tasks.isEmpty() || !pendingTransitions.isEmpty()) {
|
||||||
while (true) {
|
while (true) {
|
||||||
processNodeToNodeTransitionQueue();
|
processNodeToNodeTransitionQueue();
|
||||||
|
@ -658,12 +657,9 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
while (!tasks.isEmpty()) {
|
while (!tasks.isEmpty()) {
|
||||||
tasks.remove().run();
|
tasks.remove().run();
|
||||||
}
|
}
|
||||||
if (++index == 100) {
|
|
||||||
if (interruptor != null && !interruptor.shouldContinue()) {
|
if (interruptor != null && !interruptor.shouldContinue()) {
|
||||||
interrupted = true;
|
interrupted = true;
|
||||||
break;
|
return;
|
||||||
}
|
|
||||||
index = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,6 +243,7 @@ public class MissingItemsProcessor {
|
||||||
public void visit(CastInstruction insn) {
|
public void visit(CastInstruction insn) {
|
||||||
checkClass(insn.getLocation(), insn.getTargetType());
|
checkClass(insn.getLocation(), insn.getTargetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ClassConstantInstruction insn) {
|
public void visit(ClassConstantInstruction insn) {
|
||||||
checkClass(insn.getLocation(), insn.getConstant());
|
checkClass(insn.getLocation(), insn.getConstant());
|
||||||
|
|
53
core/src/main/java/org/teavm/vm/MemoryBuildTarget.java
Normal file
53
core/src/main/java/org/teavm/vm/MemoryBuildTarget.java
Normal file
|
@ -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<String, ByteArrayOutputStream> data = new LinkedHashMap<>();
|
||||||
|
private Set<? extends String> names = Collections.unmodifiableSet(data.keySet());
|
||||||
|
|
||||||
|
public Set<? extends String> 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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,6 +140,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
private AnnotationAwareCacheStatus cacheStatus;
|
private AnnotationAwareCacheStatus cacheStatus;
|
||||||
private ProgramDependencyExtractor programDependencyExtractor = new ProgramDependencyExtractor();
|
private ProgramDependencyExtractor programDependencyExtractor = new ProgramDependencyExtractor();
|
||||||
private List<Predicate<MethodReference>> additionalVirtualMethods = new ArrayList<>();
|
private List<Predicate<MethodReference>> additionalVirtualMethods = new ArrayList<>();
|
||||||
|
private int lastKnownClasses;
|
||||||
|
|
||||||
TeaVM(TeaVMBuilder builder) {
|
TeaVM(TeaVMBuilder builder) {
|
||||||
target = builder.target;
|
target = builder.target;
|
||||||
|
@ -336,6 +337,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return writtenClasses;
|
return writtenClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLastKnownClasses(int lastKnownClasses) {
|
||||||
|
this.lastKnownClasses = lastKnownClasses;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Does actual build. Call this method after TeaVM is fully configured and all entry points
|
* <p>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)
|
* 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);
|
target.setController(targetController);
|
||||||
|
|
||||||
// Check dependencies
|
// Check dependencies
|
||||||
reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, 1);
|
reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, lastKnownClasses > 0 ? lastKnownClasses : 1);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported());
|
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);
|
target.contributeDependencies(dependencyAnalyzer);
|
||||||
dependencyAnalyzer.processDependencies();
|
dependencyAnalyzer.processDependencies();
|
||||||
if (wasCancelled() || !diagnostics.getSevereProblems().isEmpty()) {
|
if (wasCancelled() || !diagnostics.getSevereProblems().isEmpty()) {
|
||||||
|
@ -368,7 +376,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
reportPhase(TeaVMPhase.LINKING, 1);
|
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -379,28 +386,36 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize and allocate registers
|
// 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) {
|
if (optimizationLevel != TeaVMOptimizationLevel.SIMPLE) {
|
||||||
devirtualize(classSet, dependencyAnalyzer);
|
progress = devirtualize(progress, classSet, dependencyAnalyzer);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyAnalyzer.cleanup();
|
dependencyAnalyzer.cleanup();
|
||||||
inline(classSet, dependencyAnalyzer);
|
progress = inline(progress, classSet, dependencyAnalyzer);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
optimize(classSet);
|
optimize(progress, classSet);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
try {
|
try {
|
||||||
|
reportPhase(TeaVMPhase.RENDERING, 1000);
|
||||||
target.emit(classSet, buildTarget, outputName);
|
target.emit(classSet, buildTarget, outputName);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Error generating output files", 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()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return progress;
|
||||||
}
|
}
|
||||||
Devirtualization devirtualization = new Devirtualization(dependency, classes);
|
Devirtualization devirtualization = new Devirtualization(dependency, classes);
|
||||||
|
int index = 0;
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
ClassHolder cls = classes.get(className);
|
ClassHolder cls = classes.get(className);
|
||||||
for (final MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getProgram() != null) {
|
if (method.getProgram() != null) {
|
||||||
devirtualization.apply(method);
|
devirtualization.apply(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
progressListener.progressReached(++index);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtualMethods = devirtualization.getVirtualMethods();
|
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) {
|
if (optimizationLevel != TeaVMOptimizationLevel.FULL) {
|
||||||
return;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<MethodReference, Program> inlinedPrograms = new HashMap<>();
|
Map<MethodReference, Program> inlinedPrograms = new HashMap<>();
|
||||||
|
@ -479,8 +497,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
inlinedPrograms.put(method.getReference(), program);
|
inlinedPrograms.put(method.getReference(), program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
progressListener.progressReached(++progress);
|
||||||
if (wasCancelled()) {
|
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()) {
|
for (String className : classSource.getClassNames()) {
|
||||||
ClassHolder cls = classSource.get(className);
|
ClassHolder cls = classSource.get(className);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
processMethod(method, classSource);
|
processMethod(method, classSource);
|
||||||
}
|
}
|
||||||
|
progressListener.progressReached(++progress);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMethod(MethodHolder method, ListableClassReaderSource classSource) {
|
private void processMethod(MethodHolder method, ListableClassReaderSource classSource) {
|
||||||
|
@ -700,5 +723,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
public boolean isVirtual(MethodReference method) {
|
public boolean isVirtual(MethodReference method) {
|
||||||
return virtualMethods == null || virtualMethods.contains(method);
|
return virtualMethods == null || virtualMethods.contains(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TeaVMProgressFeedback reportProgress(int progres) {
|
||||||
|
return progressListener.progressReached(progres);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,5 @@ public enum TeaVMPhase {
|
||||||
DEPENDENCY_ANALYSIS,
|
DEPENDENCY_ANALYSIS,
|
||||||
LINKING,
|
LINKING,
|
||||||
OPTIMIZATION,
|
OPTIMIZATION,
|
||||||
DECOMPILATION,
|
|
||||||
RENDERING
|
RENDERING
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,4 +49,6 @@ public interface TeaVMTargetController {
|
||||||
Set<? extends String> getPreservedClasses();
|
Set<? extends String> getPreservedClasses();
|
||||||
|
|
||||||
boolean isVirtual(MethodReference method);
|
boolean isVirtual(MethodReference method);
|
||||||
|
|
||||||
|
TeaVMProgressFeedback reportProgress(int progress);
|
||||||
}
|
}
|
||||||
|
|
3
pom.xml
3
pom.xml
|
@ -66,7 +66,7 @@
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<sonatypeOssDistMgmtSnapshotsUrl>https://oss.sonatype.org/content/repositories/snapshots/</sonatypeOssDistMgmtSnapshotsUrl>
|
<sonatypeOssDistMgmtSnapshotsUrl>https://oss.sonatype.org/content/repositories/snapshots/</sonatypeOssDistMgmtSnapshotsUrl>
|
||||||
<html4j.version>1.5</html4j.version>
|
<html4j.version>1.5</html4j.version>
|
||||||
<jetty.version>9.2.1.v20140609</jetty.version>
|
<jetty.version>9.4.14.v20181114</jetty.version>
|
||||||
<slf4j.version>1.7.7</slf4j.version>
|
<slf4j.version>1.7.7</slf4j.version>
|
||||||
<jackson.version>2.6.2</jackson.version>
|
<jackson.version>2.6.2</jackson.version>
|
||||||
<idea.version>2017.3.5</idea.version>
|
<idea.version>2017.3.5</idea.version>
|
||||||
|
@ -93,6 +93,7 @@
|
||||||
<module>tools/maven</module>
|
<module>tools/maven</module>
|
||||||
<module>tools/chrome-rdp</module>
|
<module>tools/chrome-rdp</module>
|
||||||
<module>tools/junit</module>
|
<module>tools/junit</module>
|
||||||
|
<module>tools/devserver</module>
|
||||||
<module>tests</module>
|
<module>tests</module>
|
||||||
<module>extras-slf4j</module>
|
<module>extras-slf4j</module>
|
||||||
<module>metaprogramming/impl</module>
|
<module>metaprogramming/impl</module>
|
||||||
|
|
|
@ -44,6 +44,7 @@ public final class BenchmarkStarter {
|
||||||
private static double timeSpentCalculating;
|
private static double timeSpentCalculating;
|
||||||
private static double totalTime;
|
private static double totalTime;
|
||||||
|
|
||||||
|
|
||||||
private BenchmarkStarter() {
|
private BenchmarkStarter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,15 +50,11 @@
|
||||||
<groupId>javax.websocket</groupId>
|
<groupId>javax.websocket</groupId>
|
||||||
<artifactId>javax.websocket-api</artifactId>
|
<artifactId>javax.websocket-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.jackson</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-core-asl</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>1.9.13</version>
|
<optional>true</optional>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.codehaus.jackson</groupId>
|
|
||||||
<artifactId>jackson-mapper-asl</artifactId>
|
|
||||||
<version>1.9.13</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp;
|
package org.teavm.chromerdp;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -29,8 +31,6 @@ import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import org.codehaus.jackson.JsonNode;
|
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.teavm.chromerdp.data.CallArgumentDTO;
|
import org.teavm.chromerdp.data.CallArgumentDTO;
|
||||||
|
@ -149,7 +149,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Message message = mapper.reader(Message.class).readValue(messageText);
|
Message message = mapper.readerFor(Message.class).readValue(messageText);
|
||||||
if (message.getMethod() == null) {
|
if (message.getMethod() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
|
|
||||||
CallFunctionResponse response = callMethod("Runtime.callFunctionOn", CallFunctionResponse.class, params);
|
CallFunctionResponse response = callMethod("Runtime.callFunctionOn", CallFunctionResponse.class, params);
|
||||||
RemoteObjectDTO result = response != null ? response.getResult() : null;
|
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) {
|
String getRepresentation(String objectId) {
|
||||||
|
@ -417,7 +417,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
|
|
||||||
CallFunctionResponse response = callMethod("Runtime.callFunctionOn", CallFunctionResponse.class, params);
|
CallFunctionResponse response = callMethod("Runtime.callFunctionOn", CallFunctionResponse.class, params);
|
||||||
RemoteObjectDTO result = response != null ? response.getResult() : null;
|
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<RDPLocalVariable> parseProperties(PropertyDescriptorDTO[] properties) {
|
private List<RDPLocalVariable> parseProperties(PropertyDescriptorDTO[] properties) {
|
||||||
|
@ -452,7 +452,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T parseJson(Class<T> type, JsonNode node) throws IOException {
|
private <T> T parseJson(Class<T> type, JsonNode node) throws IOException {
|
||||||
return mapper.reader(type).readValue(node);
|
return mapper.readerFor(type).readValue(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(Message message) {
|
private void sendMessage(Message message) {
|
||||||
|
@ -516,7 +516,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
out.complete(null);
|
out.complete(null);
|
||||||
} else {
|
} 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);
|
out.complete(response);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp;
|
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.Decoder;
|
||||||
import javax.websocket.Encoder;
|
import javax.websocket.Encoder;
|
||||||
import javax.websocket.Extension;
|
import javax.websocket.Extension;
|
||||||
|
@ -57,9 +60,8 @@ public class ChromeRDPServer {
|
||||||
context.setContextPath("/");
|
context.setContextPath("/");
|
||||||
server.setHandler(context);
|
server.setHandler(context);
|
||||||
|
|
||||||
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
|
||||||
wscontainer.addEndpoint(new RPDEndpointConfig());
|
wscontainer.addEndpoint(new RPDEndpointConfig());
|
||||||
server.start();
|
server.start();
|
||||||
server.join();
|
server.join();
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class CallArgumentDTO {
|
public class CallArgumentDTO {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class CallFrameDTO {
|
public class CallFrameDTO {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class LocationDTO {
|
public class LocationDTO {
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class Message {
|
public class Message {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class PropertyDescriptorDTO {
|
public class PropertyDescriptorDTO {
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class RemoteObjectDTO {
|
public class RemoteObjectDTO {
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class Response {
|
public class Response {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.data;
|
package org.teavm.chromerdp.data;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class ScopeDTO {
|
public class ScopeDTO {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.teavm.chromerdp.data.CallArgumentDTO;
|
import org.teavm.chromerdp.data.CallArgumentDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.teavm.chromerdp.data.RemoteObjectDTO;
|
import org.teavm.chromerdp.data.RemoteObjectDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class CompileScriptCommand {
|
public class CompileScriptCommand {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class CompileScriptResponse {
|
public class CompileScriptResponse {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.teavm.chromerdp.data.LocationDTO;
|
import org.teavm.chromerdp.data.LocationDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class GetPropertiesCommand {
|
public class GetPropertiesCommand {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.teavm.chromerdp.data.PropertyDescriptorDTO;
|
import org.teavm.chromerdp.data.PropertyDescriptorDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class RemoveBreakpointCommand {
|
public class RemoveBreakpointCommand {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class RunScriptCommand {
|
public class RunScriptCommand {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class ScriptParsedNotification {
|
public class ScriptParsedNotification {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.teavm.chromerdp.data.LocationDTO;
|
import org.teavm.chromerdp.data.LocationDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.teavm.chromerdp.data.LocationDTO;
|
import org.teavm.chromerdp.data.LocationDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp.messages;
|
package org.teavm.chromerdp.messages;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.codehaus.jackson.JsonNode;
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
|
||||||
import org.teavm.chromerdp.data.CallFrameDTO;
|
import org.teavm.chromerdp.data.CallFrameDTO;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -51,6 +51,11 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-devserver</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.teavm</groupId>
|
<groupId>org.teavm</groupId>
|
||||||
<artifactId>teavm-jso-impl</artifactId>
|
<artifactId>teavm-jso-impl</artifactId>
|
||||||
|
|
163
tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java
Normal file
163
tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,24 +20,7 @@ import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
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.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.CommandLine;
|
||||||
import org.apache.commons.cli.CommandLineParser;
|
import org.apache.commons.cli.CommandLineParser;
|
||||||
import org.apache.commons.cli.HelpFormatter;
|
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.ParseException;
|
||||||
import org.apache.commons.cli.PosixParser;
|
import org.apache.commons.cli.PosixParser;
|
||||||
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||||
|
import org.teavm.tooling.ConsoleTeaVMToolLog;
|
||||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
import org.teavm.tooling.TeaVMTargetType;
|
import org.teavm.tooling.TeaVMTargetType;
|
||||||
import org.teavm.tooling.TeaVMTool;
|
import org.teavm.tooling.TeaVMTool;
|
||||||
import org.teavm.tooling.TeaVMToolException;
|
import org.teavm.tooling.TeaVMToolException;
|
||||||
|
import org.teavm.tooling.util.FileSystemWatcher;
|
||||||
import org.teavm.vm.TeaVMOptimizationLevel;
|
import org.teavm.vm.TeaVMOptimizationLevel;
|
||||||
import org.teavm.vm.TeaVMPhase;
|
import org.teavm.vm.TeaVMPhase;
|
||||||
import org.teavm.vm.TeaVMProgressFeedback;
|
import org.teavm.vm.TeaVMProgressFeedback;
|
||||||
|
@ -58,7 +43,7 @@ import org.teavm.vm.TeaVMProgressListener;
|
||||||
public final class TeaVMRunner {
|
public final class TeaVMRunner {
|
||||||
private static Options options = new Options();
|
private static Options options = new Options();
|
||||||
private TeaVMTool tool = new TeaVMTool();
|
private TeaVMTool tool = new TeaVMTool();
|
||||||
private AccumulatingTeaVMToolLog log = new AccumulatingTeaVMToolLog(new ConsoleTeaVMToolLog());
|
private AccumulatingTeaVMToolLog log = new AccumulatingTeaVMToolLog(new ConsoleTeaVMToolLog(false));
|
||||||
private CommandLine commandLine;
|
private CommandLine commandLine;
|
||||||
private long startTime;
|
private long startTime;
|
||||||
private long phaseStartTime;
|
private long phaseStartTime;
|
||||||
|
@ -97,12 +82,6 @@ public final class TeaVMRunner {
|
||||||
.hasArg()
|
.hasArg()
|
||||||
.withArgName("number")
|
.withArgName("number")
|
||||||
.create("O"));
|
.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
|
options.addOption(OptionBuilder
|
||||||
.withDescription("Generate debug information")
|
.withDescription("Generate debug information")
|
||||||
.withLongOpt("debug")
|
.withLongOpt("debug")
|
||||||
|
@ -340,30 +319,28 @@ public final class TeaVMRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildInteractive() {
|
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) {
|
while (true) {
|
||||||
ProgressListenerImpl progressListener = new ProgressListenerImpl();
|
ProgressListenerImpl progressListener = new ProgressListenerImpl(watcher);
|
||||||
Thread thread = null;
|
|
||||||
try {
|
try {
|
||||||
watcher.progressListener = progressListener;
|
|
||||||
thread = new Thread(watcher);
|
|
||||||
thread.start();
|
|
||||||
if (progressListener.cancelRequested.get()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
build(progressListener);
|
build(progressListener);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
} finally {
|
|
||||||
if (!progressListener.cancelRequested.get()) {
|
|
||||||
thread.interrupt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
System.out.println("Waiting for changes...");
|
System.out.println("Waiting for changes...");
|
||||||
watcher.waitForChange();
|
watcher.waitForChange(750);
|
||||||
|
watcher.grabChangedFiles();
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println("Changes detected. Recompiling...");
|
System.out.println("Changes detected. Recompiling...");
|
||||||
} catch (InterruptedException | IOException e) {
|
} 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<WatchKey, Path> keysToPath = new HashMap<>();
|
|
||||||
private Map<Path, WatchKey> 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<Path>() {
|
|
||||||
@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<Path> filter(WatchKey key) throws IOException {
|
|
||||||
List<Path> 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() {
|
private void buildNonInteractive() {
|
||||||
try {
|
try {
|
||||||
build(new ProgressListenerImpl());
|
build(new ProgressListenerImpl(null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
System.exit(-2);
|
System.exit(-2);
|
||||||
|
@ -561,12 +393,17 @@ public final class TeaVMRunner {
|
||||||
|
|
||||||
class ProgressListenerImpl implements TeaVMProgressListener {
|
class ProgressListenerImpl implements TeaVMProgressListener {
|
||||||
private TeaVMPhase currentPhase;
|
private TeaVMPhase currentPhase;
|
||||||
AtomicBoolean cancelRequested = new AtomicBoolean();
|
private FileSystemWatcher fileSystemWatcher;
|
||||||
|
|
||||||
|
ProgressListenerImpl(FileSystemWatcher fileSystemWatcher) {
|
||||||
|
this.fileSystemWatcher = fileSystemWatcher;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TeaVMProgressFeedback progressReached(int progress) {
|
public TeaVMProgressFeedback progressReached(int progress) {
|
||||||
return cancelRequested.get() ? TeaVMProgressFeedback.CANCEL : TeaVMProgressFeedback.CONTINUE;
|
return getStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) {
|
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) {
|
||||||
log.flush();
|
log.flush();
|
||||||
|
@ -585,16 +422,25 @@ public final class TeaVMRunner {
|
||||||
case OPTIMIZATION:
|
case OPTIMIZATION:
|
||||||
System.out.print("Optimizing code...");
|
System.out.print("Optimizing code...");
|
||||||
break;
|
break;
|
||||||
case DECOMPILATION:
|
|
||||||
System.out.print("Decompiling...");
|
|
||||||
break;
|
|
||||||
case RENDERING:
|
case RENDERING:
|
||||||
System.out.print("Generating output...");
|
System.out.print("Generating output...");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
currentPhase = phase;
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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
|
@Override
|
||||||
public void info(String text) {
|
public void info(String text) {
|
||||||
System.out.println("INFO: " + text);
|
System.out.println("INFO: " + text);
|
||||||
|
@ -25,8 +29,10 @@ class ConsoleTeaVMToolLog implements TeaVMToolLog {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debug(String text) {
|
public void debug(String text) {
|
||||||
|
if (debug) {
|
||||||
System.out.println("DEBUG: " + text);
|
System.out.println("DEBUG: " + text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void warning(String text) {
|
public void warning(String text) {
|
||||||
|
@ -46,9 +52,11 @@ class ConsoleTeaVMToolLog implements TeaVMToolLog {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debug(String text, Throwable e) {
|
public void debug(String text, Throwable e) {
|
||||||
|
if (debug) {
|
||||||
System.out.println("DEBUG: " + text);
|
System.out.println("DEBUG: " + text);
|
||||||
e.printStackTrace(System.out);
|
e.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void warning(String text, Throwable e) {
|
public void warning(String text, Throwable e) {
|
|
@ -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<WatchKey, Path> keysToPath = new HashMap<>();
|
||||||
|
private Map<Path, WatchKey> pathsToKey = new HashMap<>();
|
||||||
|
private Set<File> 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<Path> register(Path path) throws IOException {
|
||||||
|
List<Path> files = new ArrayList<>();
|
||||||
|
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
|
||||||
|
@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<File> grabChangedFiles() {
|
||||||
|
List<File> 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<Path> 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<Path> 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<Path> 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;
|
||||||
|
}
|
||||||
|
}
|
129
tools/devserver/pom.xml
Normal file
129
tools/devserver/pom.xml
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm</artifactId>
|
||||||
|
<version>0.6.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../..</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>teavm-devserver</artifactId>
|
||||||
|
|
||||||
|
<name>TeaVM Dev Sever</name>
|
||||||
|
<description>TeaVM development server that builds and serves JavaScript files</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm-commons</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm-util</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.carrotsearch</groupId>
|
||||||
|
<artifactId>hppc</artifactId>
|
||||||
|
<version>0.7.3</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mozilla</groupId>
|
||||||
|
<artifactId>rhino</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-tooling</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-classlib</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-metaprogramming-impl</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-jso-impl</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||||
|
<artifactId>javax-websocket-server-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>../../checkstyle.xml</configLocation>
|
||||||
|
<propertyExpansion>config_loc=${basedir}/../..</propertyExpansion>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -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<InputStream> EMPTY_CONTENT = () -> null;
|
||||||
|
|
||||||
|
private String mainClass;
|
||||||
|
private String[] classPath;
|
||||||
|
private String fileName = "classes.js";
|
||||||
|
private String pathToFile = "/";
|
||||||
|
private List<String> sourcePath = new ArrayList<>();
|
||||||
|
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||||
|
private boolean indicator;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
private Map<String, Supplier<InputStream>> 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<String, byte[]> 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<String> 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<InputStream> findSourceFile(String fileName) {
|
||||||
|
for (String element : sourcePath) {
|
||||||
|
File sourceFile = new File(element);
|
||||||
|
if (sourceFile.isFile()) {
|
||||||
|
Supplier<InputStream> 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<InputStream> 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<String> 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<String> getChangedClasses(Collection<File> changedFiles) {
|
||||||
|
List<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<CodeWsEndpoint> consumer = (Consumer<CodeWsEndpoint>) 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 + " }");
|
||||||
|
}
|
||||||
|
}
|
170
tools/devserver/src/main/java/org/teavm/devserver/DevServer.java
Normal file
170
tools/devserver/src/main/java/org/teavm/devserver/DevServer.java
Normal file
|
@ -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<String> 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<String> 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<String, Object> userProperties = new HashMap<>();
|
||||||
|
|
||||||
|
public DevServerEndpointConfig(Consumer<CodeWsEndpoint> consumer) {
|
||||||
|
userProperties.put("ws.consumer", consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Decoder>> getDecoders() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Encoder>> getEncoders() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getUserProperties() {
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configurator getConfigurator() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getEndpointClass() {
|
||||||
|
return CodeWsEndpoint.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Extension> getExtensions() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return pathToFile + fileName + ".ws";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getSubprotocols() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
})();
|
|
@ -182,8 +182,6 @@ class TeaVMBuild {
|
||||||
return "Discovering classes to compile";
|
return "Discovering classes to compile";
|
||||||
case LINKING:
|
case LINKING:
|
||||||
return "Resolving method invocations";
|
return "Resolving method invocations";
|
||||||
case DECOMPILATION:
|
|
||||||
return "Compiling classes";
|
|
||||||
case OPTIMIZATION:
|
case OPTIMIZATION:
|
||||||
return "Optimizing code";
|
return "Optimizing code";
|
||||||
case RENDERING:
|
case RENDERING:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user