Start implementing async JS generator

This commit is contained in:
Alexey Andreev 2015-01-30 19:15:12 +04:00
parent c546af553b
commit 5593aa074e
12 changed files with 391 additions and 27 deletions

View File

@ -23,7 +23,7 @@ import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.InjectedBy; import org.teavm.javascript.ni.InjectedBy;
import org.teavm.javascript.ni.PreserveOriginalName; import org.teavm.javascript.ni.PreserveOriginalName;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.AsyncProgramSplitter;
/** /**
* *
@ -153,11 +153,16 @@ public class Decompiler {
} }
public MethodNode decompile(MethodHolder method) { public MethodNode decompile(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) : return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method, false) :
decompileRegular(method); decompileRegular(method);
} }
public NativeMethodNode decompileNative(MethodHolder method) { public MethodNode decompileAsync(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method, true) :
decompileAsync(method);
}
public NativeMethodNode decompileNative(MethodHolder method, boolean async) {
Generator generator = generators.get(method.getReference()); Generator generator = generators.get(method.getReference());
if (generator == null) { if (generator == null) {
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName()); AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
@ -179,10 +184,12 @@ public class Decompiler {
method.getDescriptor())); method.getDescriptor()));
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers())); methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
methodNode.setGenerator(generator); methodNode.setGenerator(generator);
methodNode.setAsync(async);
return methodNode; return methodNode;
} }
public RegularMethodNode decompileRegular(MethodHolder method) { public RegularMethodNode decompileRegular(MethodHolder method) {
// TODO: add caching in case of incremental build
if (regularMethodCache == null) { if (regularMethodCache == null) {
return decompileRegularCacheMiss(method); return decompileRegularCacheMiss(method);
} }
@ -191,17 +198,52 @@ public class Decompiler {
node = decompileRegularCacheMiss(method); node = decompileRegularCacheMiss(method);
regularMethodCache.store(method.getReference(), node); regularMethodCache.store(method.getReference(), node);
} }
// TODO: add optimization
node.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, method.getProgram().variableCount());
for (int i = 0; i < paramCount; ++i) {
Variable var = method.getProgram().variableAt(i);
node.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
}
return node;
}
public AsyncMethodNode decompileRegularAsync(MethodHolder method) {
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
AsyncProgramSplitter splitter = new AsyncProgramSplitter();
splitter.split(method.getProgram());
for (int i = 0; i < splitter.size(); ++i) {
AsyncMethodPart part = new AsyncMethodPart();
part.setInputVariable(splitter.getInput(i));
part.setStatement(getRegularMethodStatement(splitter.getProgram(i)));
}
return node; return node;
} }
public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) { public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) {
RegularMethodNode methodNode = new RegularMethodNode(method.getReference());
Program program = method.getProgram();
methodNode.setBody(getRegularMethodStatement(program));
for (int i = 0; i < program.variableCount(); ++i) {
methodNode.getVariables().add(program.variableAt(i).getRegister());
}
Optimizer optimizer = new Optimizer();
optimizer.optimize(methodNode, method.getProgram());
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, program.variableCount());
for (int i = 0; i < paramCount; ++i) {
Variable var = program.variableAt(i);
methodNode.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
}
return methodNode;
}
private Statement getRegularMethodStatement(Program program) {
lastBlockId = 1; lastBlockId = 1;
graph = ProgramUtils.buildControlFlowGraph(method.getProgram());
indexer = new GraphIndexer(graph); indexer = new GraphIndexer(graph);
graph = indexer.getGraph(); graph = indexer.getGraph();
loopGraph = new LoopGraph(this.graph); loopGraph = new LoopGraph(this.graph);
unflatCode(); unflatCode();
Program program = method.getProgram();
blockMap = new Block[program.basicBlockCount() * 2 + 1]; blockMap = new Block[program.basicBlockCount() * 2 + 1];
Deque<Block> stack = new ArrayDeque<>(); Deque<Block> stack = new ArrayDeque<>();
BlockStatement rootStmt = new BlockStatement(); BlockStatement rootStmt = new BlockStatement();
@ -276,22 +318,7 @@ public class Decompiler {
} }
SequentialStatement result = new SequentialStatement(); SequentialStatement result = new SequentialStatement();
result.getSequence().addAll(rootStmt.getBody()); result.getSequence().addAll(rootStmt.getBody());
MethodReference reference = new MethodReference(method.getOwnerName(), method.getDescriptor()); return result;
RegularMethodNode methodNode = new RegularMethodNode(reference);
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
methodNode.setBody(result);
for (int i = 0; i < program.variableCount(); ++i) {
methodNode.getVariables().add(program.variableAt(i).getRegister());
}
Optimizer optimizer = new Optimizer();
optimizer.optimize(methodNode, method.getProgram());
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, program.variableCount());
for (int i = 0; i < paramCount; ++i) {
Variable var = program.variableAt(i);
methodNode.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
}
return methodNode;
} }
private Set<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) { private Set<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) {

View File

@ -53,6 +53,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private Deque<LocationStackEntry> locationStack = new ArrayDeque<>(); private Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
private DeferredCallSite lastCallSite; private DeferredCallSite lastCallSite;
private DeferredCallSite prevCallSite; private DeferredCallSite prevCallSite;
private boolean async;
private static class InjectorHolder { private static class InjectorHolder {
public final Injector injector; public final Injector injector;
@ -537,6 +538,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override @Override
public void visit(RegularMethodNode method) { public void visit(RegularMethodNode method) {
try { try {
Renderer.this.async = false;
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
for (int i = 0; i < method.getParameterDebugNames().size(); ++i) { for (int i = 0; i < method.getParameterDebugNames().size(); ++i) {
debugEmitter.emitVariable(method.getParameterDebugNames().get(i).toArray(new String[0]), debugEmitter.emitVariable(method.getParameterDebugNames().get(i).toArray(new String[0]),
@ -572,6 +574,50 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
} }
@Override
public void visit(AsyncMethodNode methodNode) {
try {
Renderer.this.async = true;
MethodReference ref = methodNode.getReference();
for (int i = 0; i < methodNode.getParameterDebugNames().size(); ++i) {
debugEmitter.emitVariable(methodNode.getParameterDebugNames().get(i).toArray(new String[0]),
variableName(i));
}
int variableCount = 0;
for (int var : methodNode.getVariables()) {
variableCount = Math.max(variableCount, var + 1);
}
List<String> variableNames = new ArrayList<>();
for (int i = ref.parameterCount() + 1; i < variableCount; ++i) {
variableNames.add(variableName(i));
}
if (!variableNames.isEmpty()) {
writer.append("var ");
for (int i = 0; i < variableNames.size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
writer.append(variableNames.get(i));
}
writer.append(";").softNewLine();
}
for (int i = 0; i < methodNode.getBody().size(); ++i) {
writer.append("function $part_").append(i).append("($input,").ws().append("$return,").ws()
.append("$throw)").ws().append('{').indent().softNewLine();
AsyncMethodPart part = methodNode.getBody().get(i);
if (part.getInputVariable() != null) {
writer.append(variableName(part.getInputVariable())).ws().append('=').ws().append("$input;")
.softNewLine();
}
part.getStatement().acceptVisitor(Renderer.this);
writer.outdent().append('}').softNewLine();
}
writer.append("return $part_0;").softNewLine();
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
}
@Override @Override
public String getParameterName(int index) { public String getParameterName(int index) {
return variableName(index); return variableName(index);
@ -832,12 +878,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
} }
writer.append("return"); writer.append("return");
if (async) {
writer.append(" $return(");
}
if (statement.getResult() != null) { if (statement.getResult() != null) {
writer.append(' '); writer.append(' ');
prevCallSite = debugEmitter.emitCallSite(); prevCallSite = debugEmitter.emitCallSite();
statement.getResult().acceptVisitor(this); statement.getResult().acceptVisitor(this);
debugEmitter.emitCallSite(); debugEmitter.emitCallSite();
} }
if (async) {
writer.append(')');
}
writer.append(";").softNewLine(); writer.append(";").softNewLine();
if (statement.getLocation() != null) { if (statement.getLocation() != null) {
popLocation(); popLocation();
@ -854,7 +906,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (statement.getLocation() != null) { if (statement.getLocation() != null) {
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
} }
writer.append("$rt_throw("); if (!async) {
writer.append("$rt_throw(");
} else {
writer.append("return $throw(");
}
prevCallSite = debugEmitter.emitCallSite(); prevCallSite = debugEmitter.emitCallSite();
statement.getException().acceptVisitor(this); statement.getException().acceptVisitor(this);
writer.append(");").softNewLine(); writer.append(");").softNewLine();
@ -1336,6 +1392,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (injector != null) { if (injector != null) {
injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod()); injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod());
} else { } else {
if (expr.getAsyncTarget() != null) {
writer.append("return ");
}
if (expr.getType() == InvocationType.DYNAMIC) { if (expr.getType() == InvocationType.DYNAMIC) {
expr.getArguments().get(0).acceptVisitor(this); expr.getArguments().get(0).acceptVisitor(this);
} }
@ -1358,7 +1417,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(')');
break; break;
case SPECIAL: case SPECIAL:
writer.append(fullName).append("("); writer.append(fullName).append("(");
@ -1368,7 +1426,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(",").ws(); writer.append(",").ws();
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(")");
break; break;
case DYNAMIC: case DYNAMIC:
writer.append(".").append(name).append("("); writer.append(".").append(name).append("(");
@ -1379,7 +1436,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(')');
virtual = true; virtual = true;
break; break;
case CONSTRUCTOR: case CONSTRUCTOR:
@ -1391,9 +1447,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(')');
break; break;
} }
if (expr.getAsyncTarget() != null) {
writer.append(',').ws().append("$part_").append(expr.getAsyncTarget());
}
writer.append(')');
if (lastCallSite != null) { if (lastCallSite != null) {
if (virtual) { if (virtual) {
lastCallSite.setVirtualMethod(expr.getMethod()); lastCallSite.setVirtualMethod(expr.getMethod());

View File

@ -0,0 +1,52 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript.ast;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class AsyncMethodNode extends MethodNode {
private List<AsyncMethodPart> body = new ArrayList<>();
private List<Integer> variables = new ArrayList<>();
private List<Set<String>> parameterDebugNames = new ArrayList<>();
public AsyncMethodNode(MethodReference reference) {
super(reference);
}
public List<AsyncMethodPart> getBody() {
return body;
}
public List<Integer> getVariables() {
return variables;
}
public List<Set<String>> getParameterDebugNames() {
return parameterDebugNames;
}
@Override
public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.javascript.ast;
/**
*
* @author Alexey Andreev
*/
public class AsyncMethodPart {
private Statement statement;
private Integer inputVariable;
public Statement getStatement() {
return statement;
}
public void setStatement(Statement statement) {
this.statement = statement;
}
public Integer getInputVariable() {
return inputVariable;
}
public void setInputVariable(Integer inputVariable) {
this.inputVariable = inputVariable;
}
}

View File

@ -28,6 +28,7 @@ public class InvocationExpr extends Expr {
private MethodReference method; private MethodReference method;
private InvocationType type; private InvocationType type;
private List<Expr> arguments = new ArrayList<>(); private List<Expr> arguments = new ArrayList<>();
private Integer asyncTarget;
public MethodReference getMethod() { public MethodReference getMethod() {
return method; return method;
@ -49,6 +50,14 @@ public class InvocationExpr extends Expr {
return arguments; return arguments;
} }
public Integer getAsyncTarget() {
return asyncTarget;
}
public void setAsyncTarget(Integer asyncTarget) {
this.asyncTarget = asyncTarget;
}
@Override @Override
public void acceptVisitor(ExprVisitor visitor) { public void acceptVisitor(ExprVisitor visitor) {
visitor.visit(this); visitor.visit(this);
@ -63,6 +72,7 @@ public class InvocationExpr extends Expr {
InvocationExpr copy = new InvocationExpr(); InvocationExpr copy = new InvocationExpr();
cache.put(this, copy); cache.put(this, copy);
copy.setMethod(method); copy.setMethod(method);
copy.setAsyncTarget(asyncTarget);
for (Expr arg : arguments) { for (Expr arg : arguments) {
copy.getArguments().add(arg.clone(cache)); copy.getArguments().add(arg.clone(cache));
} }

View File

@ -22,5 +22,7 @@ package org.teavm.javascript.ast;
public interface MethodNodeVisitor { public interface MethodNodeVisitor {
void visit(RegularMethodNode methodNode); void visit(RegularMethodNode methodNode);
void visit(AsyncMethodNode methodNode);
void visit(NativeMethodNode methodNode); void visit(NativeMethodNode methodNode);
} }

View File

@ -24,6 +24,7 @@ import org.teavm.model.MethodReference;
*/ */
public class NativeMethodNode extends MethodNode { public class NativeMethodNode extends MethodNode {
private Generator generator; private Generator generator;
private boolean async;
public NativeMethodNode(MethodReference reference) { public NativeMethodNode(MethodReference reference) {
super(reference); super(reference);
@ -37,6 +38,14 @@ public class NativeMethodNode extends MethodNode {
this.generator = generator; this.generator = generator;
} }
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
@Override @Override
public void acceptVisitor(MethodNodeVisitor visitor) { public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -0,0 +1,36 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author Alexey Andreev
*/
public class AsyncInformation {
private Set<MethodReference> syncMethods = new HashSet<>();
private Set<MethodReference> asyncMethods = new HashSet<>();
public Set<MethodReference> getSyncMethods() {
return syncMethods;
}
public Set<MethodReference> getAsyncMethods() {
return asyncMethods;
}
}

View File

@ -16,8 +16,12 @@
package org.teavm.model.instructions; package org.teavm.model.instructions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import org.teavm.model.*; import java.util.Set;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Variable;
/** /**
* *
@ -28,6 +32,8 @@ public class InvokeInstruction extends Instruction {
private MethodReference method; private MethodReference method;
private Variable instance; private Variable instance;
private List<Variable> arguments = new ArrayList<>(); private List<Variable> arguments = new ArrayList<>();
private Set<MethodReference> implemenetations = new HashSet<MethodReference>();
private boolean resolved;
private Variable receiver; private Variable receiver;
public InvocationType getType() { public InvocationType getType() {
@ -66,6 +72,18 @@ public class InvokeInstruction extends Instruction {
this.receiver = receiver; this.receiver = receiver;
} }
public Set<MethodReference> getImplemenetations() {
return implemenetations;
}
public boolean isResolved() {
return resolved;
}
public void setResolved(boolean resolved) {
this.resolved = resolved;
}
@Override @Override
public void acceptVisitor(InstructionVisitor visitor) { public void acceptVisitor(InstructionVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -0,0 +1,54 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.model.Program;
/**
*
* @author Alexey Andreev
*/
public class AsyncProgramSplitter {
private List<Part> parts = new ArrayList<>();
private Map<Long, Integer> partMap = new HashMap<>();
public void split(Program program) {
parts.clear();
// TODO: implement splitting algorithm
partMap.clear();
}
public int size() {
return parts.size();
}
public Program getProgram(int index) {
return parts.get(index).program;
}
public Integer getInput(int index) {
return parts.get(index).input;
}
private static class Part {
Program program;
Integer input;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2015 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.runtime;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Alexey Andreev
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
public @interface Async {
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2015 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.runtime;
/**
*
* @author Alexey Andreev
*/
public interface AsyncCallback<T> {
void complete(T value);
void error(Exception e);
}