Place classes to a single object in development server to improve debugger performance

This commit is contained in:
Alexey Andreev 2019-02-07 16:29:55 +03:00
parent 95a3a30a6e
commit 1b2c47407f
11 changed files with 291 additions and 262 deletions

View File

@ -147,7 +147,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
writer.appendClass(className).append(".$meta.fields").ws().append('=').ws().append('[').indent(); writer.appendClass(className).append(".$meta.fields").ws().append('=').ws().append('[').indent();
generateCreateMembers(writer, cls.getFields(), field -> { generateCreateMembers(writer, cls.getFields(), field -> {
appendProperty(writer, "type", false, () -> writer.append(context.typeToClassString(field.getType()))); appendProperty(writer, "type", false, () -> context.typeToClassString(writer, field.getType()));
appendProperty(writer, "getter", false, () -> { appendProperty(writer, "getter", false, () -> {
if (accessibleFields != null && accessibleFields.contains(field.getName())) { if (accessibleFields != null && accessibleFields.contains(field.getName())) {
@ -188,13 +188,13 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
if (i > 0) { if (i > 0) {
writer.append(',').ws(); writer.append(',').ws();
} }
writer.append(context.typeToClassString(method.parameterType(i))); context.typeToClassString(writer, method.parameterType(i));
} }
writer.append(']'); writer.append(']');
}); });
appendProperty(writer, "returnType", false, () -> { appendProperty(writer, "returnType", false, () -> {
writer.append(context.typeToClassString(method.getResultType())); context.typeToClassString(writer, method.getResultType());
}); });
appendProperty(writer, "callable", false, () -> { appendProperty(writer, "callable", false, () -> {

View File

@ -358,6 +358,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderer.prepare(clsNodes); renderer.prepare(clsNodes);
runtimeRenderer.renderRuntime(); runtimeRenderer.renderRuntime();
if (classScoped) {
sourceWriter.append("var ").append(Renderer.CONTAINER_OBJECT).ws().append("=").ws()
.append("Object.create(null);").newLine();
}
if (!renderer.render(clsNodes)) { if (!renderer.render(clsNodes)) {
return; return;
} }

View File

@ -16,6 +16,7 @@
package org.teavm.backend.javascript.codegen; package org.teavm.backend.javascript.codegen;
import java.io.IOException; import java.io.IOException;
import org.teavm.backend.javascript.rendering.Renderer;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -101,11 +102,12 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendClass(String cls) throws IOException { public SourceWriter appendClass(String cls) throws IOException {
appendScopeIfNecessary();
return append(naming.getNameFor(cls)); return append(naming.getNameFor(cls));
} }
public SourceWriter appendClass(Class<?> cls) throws IOException { public SourceWriter appendClass(Class<?> cls) throws IOException {
return append(naming.getNameFor(cls.getName())); return appendClass(cls.getName());
} }
public SourceWriter appendField(FieldReference field) throws IOException { public SourceWriter appendField(FieldReference field) throws IOException {
@ -113,7 +115,7 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendStaticField(FieldReference field) throws IOException { public SourceWriter appendStaticField(FieldReference field) throws IOException {
appendClassScopeIfNecessary(field.getClassName()); appendScopeIfNecessary();
return append(naming.getFullNameFor(field)); return append(naming.getFullNameFor(field));
} }
@ -126,7 +128,7 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendMethodBody(MethodReference method) throws IOException { public SourceWriter appendMethodBody(MethodReference method) throws IOException {
appendClassScopeIfNecessary(method.getClassName()); appendScopeIfNecessary();
return append(naming.getFullNameFor(method)); return append(naming.getFullNameFor(method));
} }
@ -143,18 +145,18 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
public SourceWriter appendInit(MethodReference method) throws IOException { public SourceWriter appendInit(MethodReference method) throws IOException {
appendClassScopeIfNecessary(method.getClassName()); appendScopeIfNecessary();
return append(naming.getNameForInit(method)); return append(naming.getNameForInit(method));
} }
public SourceWriter appendClassInit(String className) throws IOException { public SourceWriter appendClassInit(String className) throws IOException {
appendClassScopeIfNecessary(className); appendScopeIfNecessary();
return append(naming.getNameForClassInit(className)); return append(naming.getNameForClassInit(className));
} }
private void appendClassScopeIfNecessary(String className) throws IOException { private void appendScopeIfNecessary() throws IOException {
if (classScoped) { if (classScoped) {
append(naming.getNameFor(className)).append("."); append(Renderer.CONTAINER_OBJECT).append(".");
} }
} }

View File

@ -60,6 +60,7 @@ import org.teavm.vm.RenderingException;
import org.teavm.vm.TeaVMProgressFeedback; import org.teavm.vm.TeaVMProgressFeedback;
public class Renderer implements RenderingManager { public class Renderer implements RenderingManager {
public static final String CONTAINER_OBJECT = "$java";
private final NamingStrategy naming; private final NamingStrategy naming;
private final SourceWriter writer; private final SourceWriter writer;
private final ListableClassReaderSource classSource; private final ListableClassReaderSource classSource;
@ -198,8 +199,9 @@ public class Renderer implements RenderingManager {
try { try {
for (PostponedFieldInitializer initializer : postponedFieldInitializers) { for (PostponedFieldInitializer initializer : postponedFieldInitializers) {
int start = writer.getOffset(); int start = writer.getOffset();
writer.appendStaticField(initializer.field).ws().append("=").ws() writer.appendStaticField(initializer.field).ws().append("=").ws();
.append(context.constantToString(initializer.value)).append(";").softNewLine(); context.constantToString(writer, initializer.value);
writer.append(";").softNewLine();
int sz = writer.getOffset() - start; int sz = writer.getOffset() - start;
appendClassSize(initializer.field.getClassName(), sz); appendClassSize(initializer.field.getClassName(), sz);
} }
@ -299,7 +301,8 @@ public class Renderer implements RenderingManager {
String jsName = naming.getNameFor(cls.getName()); String jsName = naming.getNameFor(cls.getName());
debugEmitter.addClass(jsName, cls.getName(), cls.getParentName()); debugEmitter.addClass(jsName, cls.getName(), cls.getParentName());
try { try {
writer.append("function " + jsName + "()").ws().append("{") renderFunctionDeclaration(jsName);
writer.append("()").ws().append("{")
.indent().softNewLine(); .indent().softNewLine();
boolean thisAliased = false; boolean thisAliased = false;
List<FieldNode> nonStaticFields = new ArrayList<>(); List<FieldNode> nonStaticFields = new ArrayList<>();
@ -326,7 +329,9 @@ public class Renderer implements RenderingManager {
} }
FieldReference fieldRef = new FieldReference(cls.getName(), field.getName()); FieldReference fieldRef = new FieldReference(cls.getName(), field.getName());
writer.append(thisAliased ? "a" : "this").append(".").appendField(fieldRef).ws() writer.append(thisAliased ? "a" : "this").append(".").appendField(fieldRef).ws()
.append("=").ws().append(context.constantToString(value)).append(";").softNewLine(); .append("=").ws();
context.constantToString(writer, value);
writer.append(";").softNewLine();
debugEmitter.addField(field.getName(), naming.getNameFor(fieldRef)); debugEmitter.addField(field.getName(), naming.getNameFor(fieldRef));
} }
@ -334,7 +339,11 @@ public class Renderer implements RenderingManager {
writer.append("this.$id$").ws().append('=').ws().append("0;").softNewLine(); writer.append("this.$id$").ws().append('=').ws().append("0;").softNewLine();
} }
writer.outdent().append("}").newLine(); writer.outdent().append("}");
if (classScoped) {
writer.append(";");
}
writer.newLine();
for (FieldNode field : staticFields) { for (FieldNode field : staticFields) {
Object value = field.getInitialValue(); Object value = field.getInitialValue();
@ -343,17 +352,18 @@ public class Renderer implements RenderingManager {
} }
FieldReference fieldRef = new FieldReference(cls.getName(), field.getName()); FieldReference fieldRef = new FieldReference(cls.getName(), field.getName());
if (value instanceof String) { if (value instanceof String) {
context.constantToString(value); context.lookupString((String) value);
postponedFieldInitializers.add(new PostponedFieldInitializer(fieldRef, (String) value)); postponedFieldInitializers.add(new PostponedFieldInitializer(fieldRef, (String) value));
value = null; value = null;
} }
if (classScoped) { if (classScoped) {
writer.append(jsName).append("."); writer.append(CONTAINER_OBJECT).append(".");
} else { } else {
writer.append("var "); writer.append("var ");
} }
writer.append(naming.getFullNameFor(fieldRef)).ws().append("=").ws() writer.append(naming.getFullNameFor(fieldRef)).ws().append("=").ws();
.append(context.constantToString(value)).append(";").softNewLine(); context.constantToString(writer, value);
writer.append(";").softNewLine();
} }
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occurred", e); throw new RenderingException("IO error occurred", e);
@ -373,14 +383,14 @@ public class Renderer implements RenderingManager {
for (MethodNode method : cls.getMethods()) { for (MethodNode method : cls.getMethods()) {
if (!method.getModifiers().contains(ElementModifier.STATIC)) { if (!method.getModifiers().contains(ElementModifier.STATIC)) {
if (method.getReference().getName().equals("<init>")) { if (method.getReference().getName().equals("<init>")) {
renderInitializer(cls.getName(), method); renderInitializer(method);
} }
} }
} }
} }
for (MethodNode method : cls.getMethods()) { for (MethodNode method : cls.getMethods()) {
renderBody(cls.getName(), method); renderBody(method);
} }
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occurred", e); throw new RenderingException("IO error occurred", e);
@ -392,12 +402,12 @@ public class Renderer implements RenderingManager {
throws IOException { throws IOException {
boolean isAsync = asyncMethods.contains(clinit.getReference()); boolean isAsync = asyncMethods.contains(clinit.getReference());
String clinitCalled = naming.getNameFor(cls.getName() + "_$clinitCalled");
if (isAsync) { if (isAsync) {
writer.append("var ").appendClass(cls.getName()).append("_$clinitCalled").ws().append("=").ws() writer.append("var ").append(clinitCalled).ws().append("=").ws().append("false;").softNewLine();
.append("false;").softNewLine();
} }
renderFunctionDeclaration(cls.getName(), naming.getNameForClassInit(cls.getName())); renderFunctionDeclaration(naming.getNameForClassInit(cls.getName()));
writer.append("()").ws() writer.append("()").ws()
.append("{").softNewLine().indent(); .append("{").softNewLine().indent();
@ -417,8 +427,7 @@ public class Renderer implements RenderingManager {
renderAsyncPrologue(); renderAsyncPrologue();
writer.append("case 0:").indent().softNewLine(); writer.append("case 0:").indent().softNewLine();
writer.appendClass(cls.getName()).append("_$clinitCalled").ws().append('=').ws().append("true;") writer.append(clinitCalled).ws().append('=').ws().append("true;").softNewLine();
.softNewLine();
} else { } else {
renderEraseClinit(cls); renderEraseClinit(cls);
} }
@ -690,10 +699,10 @@ public class Renderer implements RenderingManager {
return null; return null;
} }
private void renderInitializer(String className, MethodNode method) throws IOException { private void renderInitializer(MethodNode method) throws IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
renderFunctionDeclaration(className, naming.getNameForInit(ref)); renderFunctionDeclaration(naming.getNameForInit(ref));
writer.append("("); writer.append("(");
for (int i = 0; i < ref.parameterCount(); ++i) { for (int i = 0; i < ref.parameterCount(); ++i) {
if (i > 0) { if (i > 0) {
@ -774,7 +783,7 @@ public class Renderer implements RenderingManager {
writer.append(");").ws().append("}"); writer.append(");").ws().append("}");
} }
private void renderBody(String className, MethodNode method) throws IOException { private void renderBody(MethodNode method) throws IOException {
StatementRenderer statementRenderer = new StatementRenderer(context, writer); StatementRenderer statementRenderer = new StatementRenderer(context, writer);
statementRenderer.setCurrentMethod(method); statementRenderer.setCurrentMethod(method);
@ -782,7 +791,7 @@ public class Renderer implements RenderingManager {
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
String name = naming.getFullNameFor(ref); String name = naming.getFullNameFor(ref);
renderFunctionDeclaration(className, name); renderFunctionDeclaration(name);
writer.append("("); writer.append("(");
int startParam = 0; int startParam = 0;
if (method.getModifiers().contains(ElementModifier.STATIC)) { if (method.getModifiers().contains(ElementModifier.STATIC)) {
@ -808,9 +817,9 @@ public class Renderer implements RenderingManager {
longLibraryUsed |= statementRenderer.isLongLibraryUsed(); longLibraryUsed |= statementRenderer.isLongLibraryUsed();
} }
private void renderFunctionDeclaration(String className, String name) throws IOException { private void renderFunctionDeclaration(String name) throws IOException {
if (classScoped) { if (classScoped) {
writer.appendClass(className).append(".").append(name).ws().append("=").ws(); writer.append(CONTAINER_OBJECT).append(".").append(name).ws().append("=").ws();
} }
writer.append("function"); writer.append("function");
if (!classScoped) { if (!classScoped) {
@ -1074,8 +1083,12 @@ public class Renderer implements RenderingManager {
} }
@Override @Override
public String typeToClassString(ValueType type) { public void typeToClassString(SourceWriter writer, ValueType type) {
return context.typeToClsString(type); try {
context.typeToClsString(writer, type);
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
@Override @Override

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.javascript.rendering; package org.teavm.backend.javascript.rendering;
import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -27,6 +28,7 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.function.Predicate; import java.util.function.Predicate;
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.spi.InjectedBy; import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.backend.javascript.spi.Injector; import org.teavm.backend.javascript.spi.Injector;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
@ -156,73 +158,122 @@ public class RenderingContext {
return readonlyStringPool; return readonlyStringPool;
} }
public String constantToString(Object cst) { public void constantToString(SourceWriter writer, Object cst) throws IOException {
if (cst == null) { if (cst == null) {
return "null"; writer.append("null");
} }
if (cst instanceof ValueType) { if (cst instanceof ValueType) {
ValueType type = (ValueType) cst; ValueType type = (ValueType) cst;
return naming.getNameForFunction("$rt_cls") + "(" + typeToClsString(type) + ")"; writer.appendFunction("$rt_cls").append("(");
typeToClsString(writer, type);
writer.append(")");
} else if (cst instanceof String) { } else if (cst instanceof String) {
String string = (String) cst; String string = (String) cst;
int index = lookupString(string); int index = lookupString(string);
return naming.getNameForFunction("$rt_s") + "(" + index + ")"; writer.appendFunction("$rt_s").append("(" + index + ")");
} else if (cst instanceof Long) { } else if (cst instanceof Long) {
long value = (Long) cst; long value = (Long) cst;
if (value == 0) { if (value == 0) {
return "Long_ZERO"; writer.append("Long_ZERO");
} else if ((int) value == value) { } else if ((int) value == value) {
return "Long_fromInt(" + value + ")"; writer.append("Long_fromInt(" + value + ")");
} else { } else {
return "new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")"; writer.append("new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")");
} }
} else if (cst instanceof Character) { } else if (cst instanceof Character) {
return Integer.toString((Character) cst); writer.append(Integer.toString((Character) cst));
} else if (cst instanceof Boolean) { } else if (cst instanceof Boolean) {
return (Boolean) cst ? "1" : "0"; writer.append((Boolean) cst ? "1" : "0");
} else { } else if (cst instanceof Integer) {
return cst.toString(); int value = (Integer) cst;
if (value < 0) {
writer.append("(");
writer.append(Integer.toString(value));
writer.append(")");
} else {
writer.append(Integer.toString(value));
}
} else if (cst instanceof Byte) {
int value = (Byte) cst;
if (value < 0) {
writer.append("(");
writer.append(Integer.toString(value));
writer.append(")");
} else {
writer.append(Integer.toString(value));
}
} else if (cst instanceof Short) {
int value = (Short) cst;
if (value < 0) {
writer.append("(");
writer.append(Integer.toString(value));
writer.append(")");
} else {
writer.append(Integer.toString(value));
}
} else if (cst instanceof Double) {
double value = (Double) cst;
if (value < 0) {
writer.append("(");
writer.append(Double.toString(value));
writer.append(")");
} else {
writer.append(Double.toString(value));
}
} else if (cst instanceof Float) {
float value = (Float) cst;
if (value < 0) {
writer.append("(");
writer.append(Float.toString(value));
writer.append(")");
} else {
writer.append(Float.toString(value));
}
} }
} }
public String typeToClsString(ValueType type) { public void typeToClsString(SourceWriter writer, ValueType type) throws IOException {
int arrayCount = 0; int arrayCount = 0;
while (type instanceof ValueType.Array) { while (type instanceof ValueType.Array) {
arrayCount++; arrayCount++;
type = ((ValueType.Array) type).getItemType(); type = ((ValueType.Array) type).getItemType();
} }
String value;
for (int i = 0; i < arrayCount; ++i) {
writer.append("$rt_arraycls(");
}
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
ValueType.Object objType = (ValueType.Object) type; ValueType.Object objType = (ValueType.Object) type;
value = naming.getNameFor(objType.getClassName()); writer.appendClass(objType.getClassName());
} else if (type instanceof ValueType.Void) { } else if (type instanceof ValueType.Void) {
value = "$rt_voidcls()"; writer.append("$rt_voidcls()");
} else if (type instanceof ValueType.Primitive) { } else if (type instanceof ValueType.Primitive) {
ValueType.Primitive primitiveType = (ValueType.Primitive) type; ValueType.Primitive primitiveType = (ValueType.Primitive) type;
switch (primitiveType.getKind()) { switch (primitiveType.getKind()) {
case BOOLEAN: case BOOLEAN:
value = "$rt_booleancls()"; writer.append("$rt_booleancls()");
break; break;
case CHARACTER: case CHARACTER:
value = "$rt_charcls()"; writer.append("$rt_charcls()");
break; break;
case BYTE: case BYTE:
value = "$rt_bytecls()"; writer.append("$rt_bytecls()");
break; break;
case SHORT: case SHORT:
value = "$rt_shortcls()"; writer.append("$rt_shortcls()");
break; break;
case INTEGER: case INTEGER:
value = "$rt_intcls()"; writer.append("$rt_intcls()");
break; break;
case LONG: case LONG:
value = "$rt_longcls()"; writer.append("$rt_longcls()");
break; break;
case FLOAT: case FLOAT:
value = "$rt_floatcls()"; writer.append("$rt_floatcls()");
break; break;
case DOUBLE: case DOUBLE:
value = "$rt_doublecls()"; writer.append("$rt_doublecls()");
break; break;
default: default:
throw new IllegalArgumentException("The type is not renderable"); throw new IllegalArgumentException("The type is not renderable");
@ -232,9 +283,8 @@ public class RenderingContext {
} }
for (int i = 0; i < arrayCount; ++i) { for (int i = 0; i < arrayCount; ++i) {
value = "$rt_arraycls(" + value + ")"; writer.append(")");
} }
return value;
} }
public String pointerName() { public String pointerName() {

View File

@ -1006,11 +1006,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
if (expr.getLocation() != null) { if (expr.getLocation() != null) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
} }
String str = context.constantToString(expr.getValue()); context.constantToString(writer, expr.getValue());
if (str.startsWith("-")) {
writer.append(' ');
}
writer.append(str);
if (expr.getLocation() != null) { if (expr.getLocation() != null) {
popLocation(); popLocation();
} }
@ -1198,7 +1194,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
precedence = Precedence.FUNCTION_CALL; precedence = Precedence.FUNCTION_CALL;
writer.append("new ").append(naming.getNameFor(expr.getConstructedClass())); writer.append("new ").appendClass(expr.getConstructedClass());
if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) { if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) {
writer.append(')'); writer.append(')');
} }
@ -1270,8 +1266,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
break; break;
} }
} else { } else {
writer.appendFunction("$rt_createArray").append("(").append(context.typeToClsString(expr.getType())) writer.appendFunction("$rt_createArray").append("(");
.append(",").ws(); context.typeToClsString(writer, expr.getType());
writer.append(",").ws();
precedence = Precedence.min(); precedence = Precedence.min();
expr.getLength().acceptVisitor(this); expr.getLength().acceptVisitor(this);
writer.append(")"); writer.append(")");
@ -1322,8 +1319,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
break; break;
} }
} else { } else {
writer.append("$rt_createMultiArray(").append(context.typeToClsString(expr.getType())) writer.append("$rt_createMultiArray(");
.append(",").ws(); context.typeToClsString(writer, expr.getType());
writer.append(",").ws();
} }
writer.append("["); writer.append("[");
boolean first = true; boolean first = true;
@ -1375,7 +1373,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
writer.appendFunction("$rt_isInstance").append("("); writer.appendFunction("$rt_isInstance").append("(");
precedence = Precedence.min(); precedence = Precedence.min();
expr.getExpr().acceptVisitor(this); expr.getExpr().acceptVisitor(this);
writer.append(",").ws().append(context.typeToClsString(expr.getType())).append(")"); writer.append(",").ws();
context.typeToClsString(writer, expr.getType());
writer.append(")");
if (expr.getLocation() != null) { if (expr.getLocation() != null) {
popLocation(); popLocation();
} }
@ -1554,16 +1554,16 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
@Override @Override
public void writeType(ValueType type) throws IOException { public void writeType(ValueType type) throws IOException {
writer.append(context.typeToClsString(type)); context.typeToClsString(writer, type);
} }
@Override @Override
public void writeExpr(Expr expr) throws IOException { public void writeExpr(Expr expr) {
writeExpr(expr, Precedence.GROUPING); writeExpr(expr, Precedence.GROUPING);
} }
@Override @Override
public void writeExpr(Expr expr, Precedence precedence) throws IOException { public void writeExpr(Expr expr, Precedence precedence) {
StatementRenderer.this.precedence = precedence; StatementRenderer.this.precedence = precedence;
expr.acceptVisitor(StatementRenderer.this); expr.acceptVisitor(StatementRenderer.this);
} }

View File

@ -16,6 +16,7 @@
package org.teavm.backend.javascript.spi; package org.teavm.backend.javascript.spi;
import java.util.Properties; import java.util.Properties;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
@ -42,7 +43,7 @@ public interface GeneratorContext extends ServiceRepository {
Diagnostics getDiagnostics(); Diagnostics getDiagnostics();
String typeToClassString(ValueType type); void typeToClassString(SourceWriter writer, ValueType type);
void useLongLibrary(); void useLongLibrary();
} }

View File

@ -39,9 +39,9 @@ public interface InjectorContext extends ServiceRepository {
void writeType(ValueType type) throws IOException; void writeType(ValueType type) throws IOException;
void writeExpr(Expr expr) throws IOException; void writeExpr(Expr expr);
void writeExpr(Expr expr, Precedence precedence) throws IOException; void writeExpr(Expr expr, Precedence precedence);
Precedence getPrecedence(); Precedence getPrecedence();

View File

@ -149,7 +149,7 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
} }
@Override @Override
protected CharSequence callMethod(String ident, String fqn, String method, String params) { protected void callMethod(String ident, String fqn, String method, String params) {
MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); MethodDescriptor desc = MethodDescriptor.parse(method + params + "V");
MethodReference methodRef = new MethodReference(fqn, desc); MethodReference methodRef = new MethodReference(fqn, desc);
@ -170,8 +170,14 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
} else { } else {
allClassesNode.addConsumer(new VirtualCallbackConsumer(agent, methodRef)); allClassesNode.addConsumer(new VirtualCallbackConsumer(agent, methodRef));
} }
}
return ""; @Override
protected void append(String text) {
}
@Override
protected void reportDiagnostic(String text) {
} }
} }

View File

@ -18,12 +18,13 @@ package org.teavm.html4j;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import net.java.html.js.JavaScriptBody; import net.java.html.js.JavaScriptBody;
import org.teavm.backend.javascript.codegen.NamingStrategy;
import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue; import org.teavm.model.AnnotationValue;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
@ -40,11 +41,6 @@ public class JavaScriptBodyGenerator implements Generator {
String body = annot.getValue("body").getString(); String body = annot.getValue("body").getString();
List<AnnotationValue> args = annot.getValue("args").getList(); List<AnnotationValue> args = annot.getValue("args").getList();
AnnotationValue javacall = annot.getValue("javacall"); AnnotationValue javacall = annot.getValue("javacall");
if (javacall != null && javacall.getBoolean()) {
GeneratorJsCallback callbackGen = new GeneratorJsCallback(context, context.getClassSource(),
writer.getNaming());
body = callbackGen.parse(body);
}
writer.append("var result = (function("); writer.append("var result = (function(");
for (int i = 0; i < args.size(); ++i) { for (int i = 0; i < args.size(); ++i) {
if (i > 0) { if (i > 0) {
@ -53,7 +49,14 @@ public class JavaScriptBodyGenerator implements Generator {
writer.append(args.get(i).getString()); writer.append(args.get(i).getString());
} }
writer.append(")").ws().append("{").indent().softNewLine(); writer.append(")").ws().append("{").indent().softNewLine();
writer.append(body).softNewLine(); if (javacall != null && javacall.getBoolean()) {
GeneratorJsCallback callbackGen = new GeneratorJsCallback(writer, context, context.getClassSource(),
context.getDiagnostics(), methodRef);
callbackGen.parse(body);
} else {
writer.append(body);
}
writer.softNewLine();
writer.outdent().append("}).call(").append(!method.hasModifier(ElementModifier.STATIC) writer.outdent().append("}).call(").append(!method.hasModifier(ElementModifier.STATIC)
? context.getParameterName(0) : "null"); ? context.getParameterName(0) : "null");
for (int i = 0; i < args.size(); ++i) { for (int i = 0; i < args.size(); ++i) {
@ -71,65 +74,88 @@ public class JavaScriptBodyGenerator implements Generator {
writer.append("(").append(param).append(")"); writer.append("(").append(param).append(")");
} }
private void unwrapValue(GeneratorContext context, SourceWriter writer, ValueType type) private void unwrapValue(GeneratorContext context, SourceWriter writer, ValueType type) throws IOException {
throws IOException {
writer.appendMethodBody(JavaScriptConvGenerator.fromJsMethod); writer.appendMethodBody(JavaScriptConvGenerator.fromJsMethod);
writer.append("(").append("result").append(",").ws().append(context.typeToClassString(type)) writer.append("(").append("result").append(",").ws();
.append(")"); context.typeToClassString(writer, type);
writer.append(")");
} }
private static class GeneratorJsCallback extends JsCallback { private static class GeneratorJsCallback extends JsCallback {
private SourceWriter writer;
private GeneratorContext context; private GeneratorContext context;
private ClassReaderSource classSource; private ClassReaderSource classSource;
private NamingStrategy naming; private Diagnostics diagnostics;
private MethodReference methodReference;
GeneratorJsCallback(GeneratorContext context, ClassReaderSource classSource, NamingStrategy naming) { GeneratorJsCallback(SourceWriter writer, GeneratorContext context, ClassReaderSource classSource,
Diagnostics diagnostics, MethodReference methodReference) {
this.writer = writer;
this.context = context; this.context = context;
this.classSource = classSource; this.classSource = classSource;
this.naming = naming; this.diagnostics = diagnostics;
this.methodReference = methodReference;
} }
@Override @Override
protected CharSequence callMethod(String ident, String fqn, String method, String params) { protected void callMethod(String ident, String fqn, String method, String params) {
MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); try {
MethodReader reader = JavaScriptBodyDependency.findMethod(classSource, fqn, desc); MethodDescriptor desc = MethodDescriptor.parse(method + params + "V");
if (reader == null) { MethodReader reader = JavaScriptBodyDependency.findMethod(classSource, fqn, desc);
return ""; if (reader == null) {
} return;
desc = reader.getDescriptor(); }
desc = reader.getDescriptor();
StringBuilder sb = new StringBuilder(); writer.append("(function(");
sb.append("(function("); if (ident != null) {
if (ident != null) { writer.append("$this");
sb.append("$this");
}
for (int i = 0; i < desc.parameterCount(); ++i) {
if (ident != null || i > 0) {
sb.append(", ");
} }
sb.append("p").append(i); for (int i = 0; i < desc.parameterCount(); ++i) {
} if (ident != null || i > 0) {
sb.append(") { return ").append(naming.getFullNameFor(JavaScriptConvGenerator.toJsMethod)).append("("); writer.append(",").ws();
if (ident == null) { }
sb.append(naming.getFullNameFor(reader.getReference())); writer.append("p").append(i);
} else {
sb.append("$this.").append(naming.getNameFor(desc));
}
sb.append("(");
for (int i = 0; i < desc.parameterCount(); ++i) {
if (i > 0) {
sb.append(", ");
} }
ValueType paramType = simplifyParamType(desc.parameterType(i)); writer.append(")").ws().append("{").ws().append("return ")
sb.append(naming.getFullNameFor(JavaScriptConvGenerator.fromJsMethod)).append("(p").append(i) .appendMethodBody(JavaScriptConvGenerator.toJsMethod).append("(");
.append(", ") if (ident == null) {
.append(context.typeToClassString(paramType)).append(")"); writer.appendMethodBody(reader.getReference());
} else {
writer.append("$this.").appendMethod(desc);
}
writer.append("(");
for (int i = 0; i < desc.parameterCount(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
ValueType paramType = simplifyParamType(desc.parameterType(i));
writer.appendMethodBody(JavaScriptConvGenerator.fromJsMethod).append("(p").append(i)
.append(",").ws();
context.typeToClassString(writer, paramType);
writer.append(")");
}
writer.append("));").ws().append("})(");
if (ident != null) {
writer.append(ident);
}
} catch (IOException e) {
throw new RuntimeException(e);
} }
sb.append(")); })("); }
if (ident != null) {
sb.append(ident); @Override
protected void append(String text) {
try {
writer.append(text);
} catch (IOException e) {
throw new RuntimeException(e);
} }
return sb.toString(); }
@Override
protected void reportDiagnostic(String text) {
diagnostics.error(new CallLocation(methodReference), text);
} }
private ValueType simplifyParamType(ValueType type) { private ValueType simplifyParamType(ValueType type) {

View File

@ -13,153 +13,80 @@
* 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.
*/ */
/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.teavm.html4j; package org.teavm.html4j;
/**
*
* @author Jaroslav Tulach <jtulach@netbeans.org>
*/
abstract class JsCallback { abstract class JsCallback {
final String parse(String body) { final void parse(String body) {
StringBuilder sb = new StringBuilder(); int pos = 0;
int pos = 0; while (true) {
for (;;) { int next = body.indexOf('@', pos);
int next = body.indexOf(".@", pos); if (next < 0) {
if (next == -1) { append(body.substring(pos));
sb.append(body.substring(pos)); break;
body = sb.toString(); }
break;
}
int ident = next;
while (ident > 0) {
if (!Character.isJavaIdentifierPart(body.charAt(--ident))) {
ident++;
break;
}
}
String refId = body.substring(ident, next);
sb.append(body.substring(pos, ident)); String refId = null;
if (next > 0 && body.charAt(next - 1) == '.') {
int ident = next - 1;
while (ident > 0) {
if (!Character.isJavaIdentifierPart(body.charAt(--ident))) {
ident++;
break;
}
}
refId = body.substring(ident, next - 1);
append(body.substring(pos, ident));
} else {
append(body.substring(pos, next));
}
int sigBeg = body.indexOf('(', next); int colon4 = body.indexOf("::", next);
int sigEnd = body.indexOf(')', sigBeg); int sigBeg = body.indexOf('(', colon4 > 0 ? colon4 : next);
int colon4 = body.indexOf("::", next); int sigEnd = body.indexOf(')', sigBeg > 0 ? sigBeg : next);
if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
throw new IllegalStateException("Wrong format of instance callback. Should be: "
+ "'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n" + body);
}
String fqn = body.substring(next + 2, colon4);
String method = body.substring(colon4 + 2, sigBeg);
String params = body.substring(sigBeg, sigEnd + 1);
int paramBeg = body.indexOf('(', sigEnd + 1); if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
if (paramBeg == -1) { reportDiagnostic("Wrong format of callback. Should be: "
throw new IllegalStateException("Wrong format of instance callback. " + "'@pkg.Class::method(Ljava/lang/Object;)(param)'" + body);
+ "Should be: 'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n" + body); append(body.substring(pos, next + 1));
} pos = next + 1;
continue;
}
sb.append(callMethod(refId, fqn, method, params)); int paramBeg = body.indexOf('(', sigEnd + 1);
if (body.charAt(paramBeg + 1) != ')') { if (paramBeg < 0 || !isWhitespaces(body, sigEnd + 1, paramBeg)) {
sb.append(","); reportDiagnostic("Wrong format of callback. Should be: "
} + "'@pkg.Class::method(Ljava/lang/Object;)(param)'" + body);
pos = paramBeg + 1; append(body.substring(pos, next + 1));
} pos = next + 1;
pos = 0; continue;
sb = null; }
for (;;) {
int next = body.indexOf("@", pos);
if (next == -1) {
if (sb == null) {
return body;
}
sb.append(body.substring(pos));
return sb.toString();
}
if (sb == null) {
sb = new StringBuilder();
}
sb.append(body.substring(pos, next)); String fqn = body.substring(next + 1, colon4);
String method = body.substring(colon4 + 2, sigBeg);
String params = body.substring(sigBeg, sigEnd + 1);
int sigBeg = body.indexOf('(', next); callMethod(refId, fqn, method, params);
int sigEnd = body.indexOf(')', sigBeg);
int colon4 = body.indexOf("::", next);
if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
throw new IllegalStateException("Wrong format of static callback. Should be: "
+ "'@pkg.Class::staticMethod(Ljava/lang/Object;)(param)':\n" + body);
}
String fqn = body.substring(next + 1, colon4);
String method = body.substring(colon4 + 2, sigBeg);
String params = body.substring(sigBeg, sigEnd + 1);
int paramBeg = body.indexOf('(', sigEnd + 1); if (refId != null && body.charAt(paramBeg + 1) != ')') {
append(",");
}
sb.append(callMethod(null, fqn, method, params)); pos = paramBeg + 1;
pos = paramBeg + 1; }
} }
}
protected abstract CharSequence callMethod( protected abstract void append(String text);
String ident, String fqn, String method, String params
);
static String mangle(String fqn, String method, String params) { protected abstract void callMethod(String ident, String fqn, String method, String params);
if (params.startsWith("(")) {
params = params.substring(1);
}
if (params.endsWith(")")) {
params = params.substring(0, params.length() - 1);
}
return
replace(fqn) + "$" + replace(method) + "$" + replace(params);
}
private static String replace(String orig) { protected abstract void reportDiagnostic(String text);
return orig.replace("_", "_1").
replace(";", "_2"). private static boolean isWhitespaces(String text, int start, int end) {
replace("[", "_3"). while (start < end) {
replace('.', '_').replace('/', '_'); if (!Character.isWhitespace(text.charAt(start++))) {
} return false;
}
}
return true;
}
} }