diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java index 5fea93452..c2b692a3a 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java @@ -147,7 +147,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin { writer.appendClass(className).append(".$meta.fields").ws().append('=').ws().append('[').indent(); 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, () -> { if (accessibleFields != null && accessibleFields.contains(field.getName())) { @@ -188,13 +188,13 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin { if (i > 0) { writer.append(',').ws(); } - writer.append(context.typeToClassString(method.parameterType(i))); + context.typeToClassString(writer, method.parameterType(i)); } writer.append(']'); }); appendProperty(writer, "returnType", false, () -> { - writer.append(context.typeToClassString(method.getResultType())); + context.typeToClassString(writer, method.getResultType()); }); appendProperty(writer, "callable", false, () -> { diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 0bd19358c..c493fa79f 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -358,6 +358,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { renderer.prepare(clsNodes); runtimeRenderer.renderRuntime(); + if (classScoped) { + sourceWriter.append("var ").append(Renderer.CONTAINER_OBJECT).ws().append("=").ws() + .append("Object.create(null);").newLine(); + } if (!renderer.render(clsNodes)) { return; } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java index 6e724cfff..a720a79df 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java @@ -16,6 +16,7 @@ package org.teavm.backend.javascript.codegen; import java.io.IOException; +import org.teavm.backend.javascript.rendering.Renderer; import org.teavm.model.FieldReference; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; @@ -101,11 +102,12 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendClass(String cls) throws IOException { + appendScopeIfNecessary(); return append(naming.getNameFor(cls)); } public SourceWriter appendClass(Class cls) throws IOException { - return append(naming.getNameFor(cls.getName())); + return appendClass(cls.getName()); } public SourceWriter appendField(FieldReference field) throws IOException { @@ -113,7 +115,7 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendStaticField(FieldReference field) throws IOException { - appendClassScopeIfNecessary(field.getClassName()); + appendScopeIfNecessary(); return append(naming.getFullNameFor(field)); } @@ -126,7 +128,7 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendMethodBody(MethodReference method) throws IOException { - appendClassScopeIfNecessary(method.getClassName()); + appendScopeIfNecessary(); return append(naming.getFullNameFor(method)); } @@ -143,18 +145,18 @@ public class SourceWriter implements Appendable, LocationProvider { } public SourceWriter appendInit(MethodReference method) throws IOException { - appendClassScopeIfNecessary(method.getClassName()); + appendScopeIfNecessary(); return append(naming.getNameForInit(method)); } public SourceWriter appendClassInit(String className) throws IOException { - appendClassScopeIfNecessary(className); + appendScopeIfNecessary(); return append(naming.getNameForClassInit(className)); } - private void appendClassScopeIfNecessary(String className) throws IOException { + private void appendScopeIfNecessary() throws IOException { if (classScoped) { - append(naming.getNameFor(className)).append("."); + append(Renderer.CONTAINER_OBJECT).append("."); } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index a2172bf6a..e0df52355 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -60,6 +60,7 @@ import org.teavm.vm.RenderingException; import org.teavm.vm.TeaVMProgressFeedback; public class Renderer implements RenderingManager { + public static final String CONTAINER_OBJECT = "$java"; private final NamingStrategy naming; private final SourceWriter writer; private final ListableClassReaderSource classSource; @@ -198,8 +199,9 @@ public class Renderer implements RenderingManager { try { for (PostponedFieldInitializer initializer : postponedFieldInitializers) { int start = writer.getOffset(); - writer.appendStaticField(initializer.field).ws().append("=").ws() - .append(context.constantToString(initializer.value)).append(";").softNewLine(); + writer.appendStaticField(initializer.field).ws().append("=").ws(); + context.constantToString(writer, initializer.value); + writer.append(";").softNewLine(); int sz = writer.getOffset() - start; appendClassSize(initializer.field.getClassName(), sz); } @@ -299,7 +301,8 @@ public class Renderer implements RenderingManager { String jsName = naming.getNameFor(cls.getName()); debugEmitter.addClass(jsName, cls.getName(), cls.getParentName()); try { - writer.append("function " + jsName + "()").ws().append("{") + renderFunctionDeclaration(jsName); + writer.append("()").ws().append("{") .indent().softNewLine(); boolean thisAliased = false; List nonStaticFields = new ArrayList<>(); @@ -326,7 +329,9 @@ public class Renderer implements RenderingManager { } FieldReference fieldRef = new FieldReference(cls.getName(), field.getName()); 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)); } @@ -334,7 +339,11 @@ public class Renderer implements RenderingManager { 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) { Object value = field.getInitialValue(); @@ -343,17 +352,18 @@ public class Renderer implements RenderingManager { } FieldReference fieldRef = new FieldReference(cls.getName(), field.getName()); if (value instanceof String) { - context.constantToString(value); + context.lookupString((String) value); postponedFieldInitializers.add(new PostponedFieldInitializer(fieldRef, (String) value)); value = null; } if (classScoped) { - writer.append(jsName).append("."); + writer.append(CONTAINER_OBJECT).append("."); } else { writer.append("var "); } - writer.append(naming.getFullNameFor(fieldRef)).ws().append("=").ws() - .append(context.constantToString(value)).append(";").softNewLine(); + writer.append(naming.getFullNameFor(fieldRef)).ws().append("=").ws(); + context.constantToString(writer, value); + writer.append(";").softNewLine(); } } catch (IOException e) { throw new RenderingException("IO error occurred", e); @@ -373,14 +383,14 @@ public class Renderer implements RenderingManager { for (MethodNode method : cls.getMethods()) { if (!method.getModifiers().contains(ElementModifier.STATIC)) { if (method.getReference().getName().equals("")) { - renderInitializer(cls.getName(), method); + renderInitializer(method); } } } } for (MethodNode method : cls.getMethods()) { - renderBody(cls.getName(), method); + renderBody(method); } } catch (IOException e) { throw new RenderingException("IO error occurred", e); @@ -392,12 +402,12 @@ public class Renderer implements RenderingManager { throws IOException { boolean isAsync = asyncMethods.contains(clinit.getReference()); + String clinitCalled = naming.getNameFor(cls.getName() + "_$clinitCalled"); if (isAsync) { - writer.append("var ").appendClass(cls.getName()).append("_$clinitCalled").ws().append("=").ws() - .append("false;").softNewLine(); + writer.append("var ").append(clinitCalled).ws().append("=").ws().append("false;").softNewLine(); } - renderFunctionDeclaration(cls.getName(), naming.getNameForClassInit(cls.getName())); + renderFunctionDeclaration(naming.getNameForClassInit(cls.getName())); writer.append("()").ws() .append("{").softNewLine().indent(); @@ -417,8 +427,7 @@ public class Renderer implements RenderingManager { renderAsyncPrologue(); writer.append("case 0:").indent().softNewLine(); - writer.appendClass(cls.getName()).append("_$clinitCalled").ws().append('=').ws().append("true;") - .softNewLine(); + writer.append(clinitCalled).ws().append('=').ws().append("true;").softNewLine(); } else { renderEraseClinit(cls); } @@ -690,10 +699,10 @@ public class Renderer implements RenderingManager { return null; } - private void renderInitializer(String className, MethodNode method) throws IOException { + private void renderInitializer(MethodNode method) throws IOException { MethodReference ref = method.getReference(); debugEmitter.emitMethod(ref.getDescriptor()); - renderFunctionDeclaration(className, naming.getNameForInit(ref)); + renderFunctionDeclaration(naming.getNameForInit(ref)); writer.append("("); for (int i = 0; i < ref.parameterCount(); ++i) { if (i > 0) { @@ -774,7 +783,7 @@ public class Renderer implements RenderingManager { 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.setCurrentMethod(method); @@ -782,7 +791,7 @@ public class Renderer implements RenderingManager { debugEmitter.emitMethod(ref.getDescriptor()); String name = naming.getFullNameFor(ref); - renderFunctionDeclaration(className, name); + renderFunctionDeclaration(name); writer.append("("); int startParam = 0; if (method.getModifiers().contains(ElementModifier.STATIC)) { @@ -808,9 +817,9 @@ public class Renderer implements RenderingManager { longLibraryUsed |= statementRenderer.isLongLibraryUsed(); } - private void renderFunctionDeclaration(String className, String name) throws IOException { + private void renderFunctionDeclaration(String name) throws IOException { if (classScoped) { - writer.appendClass(className).append(".").append(name).ws().append("=").ws(); + writer.append(CONTAINER_OBJECT).append(".").append(name).ws().append("=").ws(); } writer.append("function"); if (!classScoped) { @@ -1074,8 +1083,12 @@ public class Renderer implements RenderingManager { } @Override - public String typeToClassString(ValueType type) { - return context.typeToClsString(type); + public void typeToClassString(SourceWriter writer, ValueType type) { + try { + context.typeToClsString(writer, type); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index 440d1090f..ce6c0fa55 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.javascript.rendering; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayDeque; @@ -27,6 +28,7 @@ import java.util.Map; import java.util.Properties; import java.util.function.Predicate; 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.Injector; import org.teavm.common.ServiceRepository; @@ -156,73 +158,122 @@ public class RenderingContext { return readonlyStringPool; } - public String constantToString(Object cst) { + public void constantToString(SourceWriter writer, Object cst) throws IOException { if (cst == null) { - return "null"; + writer.append("null"); } if (cst instanceof ValueType) { 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) { String string = (String) cst; int index = lookupString(string); - return naming.getNameForFunction("$rt_s") + "(" + index + ")"; + writer.appendFunction("$rt_s").append("(" + index + ")"); } else if (cst instanceof Long) { long value = (Long) cst; if (value == 0) { - return "Long_ZERO"; + writer.append("Long_ZERO"); } else if ((int) value == value) { - return "Long_fromInt(" + value + ")"; + writer.append("Long_fromInt(" + value + ")"); } else { - return "new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")"; + writer.append("new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")"); } } else if (cst instanceof Character) { - return Integer.toString((Character) cst); + writer.append(Integer.toString((Character) cst)); } else if (cst instanceof Boolean) { - return (Boolean) cst ? "1" : "0"; - } else { - return cst.toString(); + writer.append((Boolean) cst ? "1" : "0"); + } else if (cst instanceof Integer) { + 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; while (type instanceof ValueType.Array) { arrayCount++; type = ((ValueType.Array) type).getItemType(); } - String value; + + for (int i = 0; i < arrayCount; ++i) { + writer.append("$rt_arraycls("); + } + if (type instanceof ValueType.Object) { ValueType.Object objType = (ValueType.Object) type; - value = naming.getNameFor(objType.getClassName()); + writer.appendClass(objType.getClassName()); } else if (type instanceof ValueType.Void) { - value = "$rt_voidcls()"; + writer.append("$rt_voidcls()"); } else if (type instanceof ValueType.Primitive) { ValueType.Primitive primitiveType = (ValueType.Primitive) type; switch (primitiveType.getKind()) { case BOOLEAN: - value = "$rt_booleancls()"; + writer.append("$rt_booleancls()"); break; case CHARACTER: - value = "$rt_charcls()"; + writer.append("$rt_charcls()"); break; case BYTE: - value = "$rt_bytecls()"; + writer.append("$rt_bytecls()"); break; case SHORT: - value = "$rt_shortcls()"; + writer.append("$rt_shortcls()"); break; case INTEGER: - value = "$rt_intcls()"; + writer.append("$rt_intcls()"); break; case LONG: - value = "$rt_longcls()"; + writer.append("$rt_longcls()"); break; case FLOAT: - value = "$rt_floatcls()"; + writer.append("$rt_floatcls()"); break; case DOUBLE: - value = "$rt_doublecls()"; + writer.append("$rt_doublecls()"); break; default: throw new IllegalArgumentException("The type is not renderable"); @@ -232,9 +283,8 @@ public class RenderingContext { } for (int i = 0; i < arrayCount; ++i) { - value = "$rt_arraycls(" + value + ")"; + writer.append(")"); } - return value; } public String pointerName() { diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 27d51a630..5cda0877d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -1006,11 +1006,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (expr.getLocation() != null) { pushLocation(expr.getLocation()); } - String str = context.constantToString(expr.getValue()); - if (str.startsWith("-")) { - writer.append(' '); - } - writer.append(str); + context.constantToString(writer, expr.getValue()); if (expr.getLocation() != null) { popLocation(); } @@ -1198,7 +1194,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { 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()) { writer.append(')'); } @@ -1270,8 +1266,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { break; } } else { - writer.appendFunction("$rt_createArray").append("(").append(context.typeToClsString(expr.getType())) - .append(",").ws(); + writer.appendFunction("$rt_createArray").append("("); + context.typeToClsString(writer, expr.getType()); + writer.append(",").ws(); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); @@ -1322,8 +1319,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { break; } } else { - writer.append("$rt_createMultiArray(").append(context.typeToClsString(expr.getType())) - .append(",").ws(); + writer.append("$rt_createMultiArray("); + context.typeToClsString(writer, expr.getType()); + writer.append(",").ws(); } writer.append("["); boolean first = true; @@ -1375,7 +1373,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { writer.appendFunction("$rt_isInstance").append("("); precedence = Precedence.min(); 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) { popLocation(); } @@ -1554,16 +1554,16 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void writeType(ValueType type) throws IOException { - writer.append(context.typeToClsString(type)); + context.typeToClsString(writer, type); } @Override - public void writeExpr(Expr expr) throws IOException { + public void writeExpr(Expr expr) { writeExpr(expr, Precedence.GROUPING); } @Override - public void writeExpr(Expr expr, Precedence precedence) throws IOException { + public void writeExpr(Expr expr, Precedence precedence) { StatementRenderer.this.precedence = precedence; expr.acceptVisitor(StatementRenderer.this); } diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java index a629820cd..fa612086a 100644 --- a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java @@ -16,6 +16,7 @@ package org.teavm.backend.javascript.spi; import java.util.Properties; +import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.common.ServiceRepository; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReaderSource; @@ -42,7 +43,7 @@ public interface GeneratorContext extends ServiceRepository { Diagnostics getDiagnostics(); - String typeToClassString(ValueType type); + void typeToClassString(SourceWriter writer, ValueType type); void useLongLibrary(); } diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/InjectorContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/InjectorContext.java index ab00d6e8e..16f318fb0 100644 --- a/core/src/main/java/org/teavm/backend/javascript/spi/InjectorContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/spi/InjectorContext.java @@ -39,9 +39,9 @@ public interface InjectorContext extends ServiceRepository { 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(); diff --git a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index adb3544c7..e3306960e 100644 --- a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -149,7 +149,7 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { } @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"); MethodReference methodRef = new MethodReference(fqn, desc); @@ -170,8 +170,14 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { } else { allClassesNode.addConsumer(new VirtualCallbackConsumer(agent, methodRef)); } + } - return ""; + @Override + protected void append(String text) { + } + + @Override + protected void reportDiagnostic(String text) { } } diff --git a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java index 0455afe3b..6b82de57b 100644 --- a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java +++ b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java @@ -18,12 +18,13 @@ package org.teavm.html4j; import java.io.IOException; import java.util.List; 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.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; +import org.teavm.diagnostics.Diagnostics; import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationValue; +import org.teavm.model.CallLocation; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.MethodDescriptor; @@ -40,11 +41,6 @@ public class JavaScriptBodyGenerator implements Generator { String body = annot.getValue("body").getString(); List args = annot.getValue("args").getList(); 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("); for (int i = 0; i < args.size(); ++i) { if (i > 0) { @@ -53,7 +49,14 @@ public class JavaScriptBodyGenerator implements Generator { writer.append(args.get(i).getString()); } 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) ? context.getParameterName(0) : "null"); for (int i = 0; i < args.size(); ++i) { @@ -71,65 +74,88 @@ public class JavaScriptBodyGenerator implements Generator { writer.append("(").append(param).append(")"); } - private void unwrapValue(GeneratorContext context, SourceWriter writer, ValueType type) - throws IOException { + private void unwrapValue(GeneratorContext context, SourceWriter writer, ValueType type) throws IOException { writer.appendMethodBody(JavaScriptConvGenerator.fromJsMethod); - writer.append("(").append("result").append(",").ws().append(context.typeToClassString(type)) - .append(")"); + writer.append("(").append("result").append(",").ws(); + context.typeToClassString(writer, type); + writer.append(")"); } private static class GeneratorJsCallback extends JsCallback { + private SourceWriter writer; private GeneratorContext context; 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.classSource = classSource; - this.naming = naming; + this.diagnostics = diagnostics; + this.methodReference = methodReference; } @Override - protected CharSequence callMethod(String ident, String fqn, String method, String params) { - MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); - MethodReader reader = JavaScriptBodyDependency.findMethod(classSource, fqn, desc); - if (reader == null) { - return ""; - } - desc = reader.getDescriptor(); + protected void callMethod(String ident, String fqn, String method, String params) { + try { + MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); + MethodReader reader = JavaScriptBodyDependency.findMethod(classSource, fqn, desc); + if (reader == null) { + return; + } + desc = reader.getDescriptor(); - StringBuilder sb = new StringBuilder(); - sb.append("(function("); - if (ident != null) { - sb.append("$this"); - } - for (int i = 0; i < desc.parameterCount(); ++i) { - if (ident != null || i > 0) { - sb.append(", "); + writer.append("(function("); + if (ident != null) { + writer.append("$this"); } - sb.append("p").append(i); - } - sb.append(") { return ").append(naming.getFullNameFor(JavaScriptConvGenerator.toJsMethod)).append("("); - if (ident == null) { - sb.append(naming.getFullNameFor(reader.getReference())); - } else { - sb.append("$this.").append(naming.getNameFor(desc)); - } - sb.append("("); - for (int i = 0; i < desc.parameterCount(); ++i) { - if (i > 0) { - sb.append(", "); + for (int i = 0; i < desc.parameterCount(); ++i) { + if (ident != null || i > 0) { + writer.append(",").ws(); + } + writer.append("p").append(i); } - ValueType paramType = simplifyParamType(desc.parameterType(i)); - sb.append(naming.getFullNameFor(JavaScriptConvGenerator.fromJsMethod)).append("(p").append(i) - .append(", ") - .append(context.typeToClassString(paramType)).append(")"); + writer.append(")").ws().append("{").ws().append("return ") + .appendMethodBody(JavaScriptConvGenerator.toJsMethod).append("("); + if (ident == null) { + 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) { diff --git a/html4j/src/main/java/org/teavm/html4j/JsCallback.java b/html4j/src/main/java/org/teavm/html4j/JsCallback.java index 24963866d..dc399f213 100644 --- a/html4j/src/main/java/org/teavm/html4j/JsCallback.java +++ b/html4j/src/main/java/org/teavm/html4j/JsCallback.java @@ -13,153 +13,80 @@ * See the License for the specific language governing permissions and * 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; -/** -* -* @author Jaroslav Tulach -*/ abstract class JsCallback { - final String parse(String body) { - StringBuilder sb = new StringBuilder(); - int pos = 0; - for (;;) { - int next = body.indexOf(".@", pos); - if (next == -1) { - sb.append(body.substring(pos)); - body = sb.toString(); - break; - } - int ident = next; - while (ident > 0) { - if (!Character.isJavaIdentifierPart(body.charAt(--ident))) { - ident++; - break; - } - } - String refId = body.substring(ident, next); + final void parse(String body) { + int pos = 0; + while (true) { + int next = body.indexOf('@', pos); + if (next < 0) { + append(body.substring(pos)); + break; + } - 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 sigEnd = body.indexOf(')', sigBeg); - int colon4 = body.indexOf("::", 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 colon4 = body.indexOf("::", next); + int sigBeg = body.indexOf('(', colon4 > 0 ? colon4 : next); + int sigEnd = body.indexOf(')', sigBeg > 0 ? sigBeg : next); - int paramBeg = body.indexOf('(', sigEnd + 1); - if (paramBeg == -1) { - throw new IllegalStateException("Wrong format of instance callback. " - + "Should be: 'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n" + body); - } + if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) { + reportDiagnostic("Wrong format of callback. Should be: " + + "'@pkg.Class::method(Ljava/lang/Object;)(param)'" + body); + append(body.substring(pos, next + 1)); + pos = next + 1; + continue; + } - sb.append(callMethod(refId, fqn, method, params)); - if (body.charAt(paramBeg + 1) != ')') { - sb.append(","); - } - pos = paramBeg + 1; - } - pos = 0; - 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(); - } + int paramBeg = body.indexOf('(', sigEnd + 1); + if (paramBeg < 0 || !isWhitespaces(body, sigEnd + 1, paramBeg)) { + reportDiagnostic("Wrong format of callback. Should be: " + + "'@pkg.Class::method(Ljava/lang/Object;)(param)'" + body); + append(body.substring(pos, next + 1)); + pos = next + 1; + continue; + } - 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); - 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); + callMethod(refId, fqn, method, params); - 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( - String ident, String fqn, String method, String params - ); + protected abstract void append(String text); - static String mangle(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); - } + protected abstract void callMethod(String ident, String fqn, String method, String params); - private static String replace(String orig) { - return orig.replace("_", "_1"). - replace(";", "_2"). - replace("[", "_3"). - replace('.', '_').replace('/', '_'); - } + protected abstract void reportDiagnostic(String text); + + private static boolean isWhitespaces(String text, int start, int end) { + while (start < end) { + if (!Character.isWhitespace(text.charAt(start++))) { + return false; + } + } + return true; + } }