Most of JavaScriptBody TCK tests pass

This commit is contained in:
konsoletyper 2014-02-19 13:44:27 +04:00
parent 8988d4fc64
commit 301d14e1ab
13 changed files with 307 additions and 23 deletions

View File

@ -497,8 +497,11 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
} }
protected TAbstractStringBuilder append(TObject obj) { protected TAbstractStringBuilder append(TObject obj) {
append(TString.wrap(obj != null ? obj.toString() : "null")); return append(TString.wrap(obj != null ? obj.toString() : "null"));
return this; }
protected TAbstractStringBuilder append(boolean b) {
return append(b ? TString.wrap("true") : TString.wrap("false"));
} }
private void ensureCapacity(int capacity) { private void ensureCapacity(int capacity) {

View File

@ -0,0 +1,60 @@
/*
* Copyright 2014 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.classlib.java.lang;
/**
*
* @author Alexey Andreev
*/
public class TAssertionError extends TError {
private static final long serialVersionUID = -4113942229209340013L;
public TAssertionError() {
super();
}
public TAssertionError(TString message, TThrowable cause) {
super(message, cause);
}
public TAssertionError(TObject message) {
super(TString.valueOf(message));
}
public TAssertionError(boolean message) {
super(TString.valueOf(message));
}
public TAssertionError(int message) {
super(TString.valueOf(message));
}
public TAssertionError(char message) {
super(TString.valueOf(message));
}
public TAssertionError(long message) {
super(TString.valueOf(message));
}
public TAssertionError(float message) {
super(TString.valueOf(message));
}
public TAssertionError(double message) {
super(TString.valueOf(message));
}
}

View File

@ -23,10 +23,23 @@ import org.teavm.javascript.ni.GeneratedBy;
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class TCharacter { public class TCharacter extends TObject {
public static final int MIN_RADIX = 2; public static final int MIN_RADIX = 2;
public static final int MAX_RADIX = 36; public static final int MAX_RADIX = 36;
private static int[] digitMapping; private static int[] digitMapping;
private char value;
public TCharacter(char value) {
this.value = value;
}
public char charValue() {
return value;
}
public static TCharacter valueOf(char value) {
return new TCharacter(value);
}
@GeneratedBy(CharacterNativeGenerator.class) @GeneratedBy(CharacterNativeGenerator.class)
public static native char toLowerCase(char ch); public static native char toLowerCase(char ch);

View File

@ -67,4 +67,8 @@ public class TClass<T> extends TObject {
@InjectedBy(ClassNativeGenerator.class) @InjectedBy(ClassNativeGenerator.class)
static native TClass<TInteger> intClass(); static native TClass<TInteger> intClass();
public boolean desiredAssertionStatus() {
return true;
}
} }

View File

@ -21,7 +21,37 @@ import org.teavm.javascript.ni.GeneratedBy;
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class TDouble { public class TDouble extends TNumber {
private double value;
public TDouble(double value) {
this.value = value;
}
@Override
public double doubleValue() {
return value;
}
@Override
public int intValue() {
return (int)value;
}
@Override
public long longValue() {
return (long)value;
}
@Override
public float floatValue() {
return (float)value;
}
public static TDouble valueOf(double d) {
return new TDouble(d);
}
@GeneratedBy(DoubleNativeGenerator.class) @GeneratedBy(DoubleNativeGenerator.class)
public static native boolean isNaN(double v); public static native boolean isNaN(double v);

View File

@ -0,0 +1,39 @@
/*
* Copyright 2014 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.classlib.java.lang;
/**
*
* @author Alexey Andreev
*/
public class TError extends TThrowable {
private static final long serialVersionUID = 3256540693876647405L;
public TError() {
}
public TError(TString message, TThrowable cause) {
super(message, cause);
}
public TError(TString message) {
super(message);
}
public TError(TThrowable cause) {
super(cause);
}
}

View File

@ -93,4 +93,10 @@ public class TStringBuilder extends TAbstractStringBuilder implements TAppendabl
super.append(obj); super.append(obj);
return this; return this;
} }
@Override
public TStringBuilder append(boolean b) {
super.append(b);
return this;
}
} }

View File

@ -0,0 +1,76 @@
/*
* Copyright 2014 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.classlib.java.lang;
/**
*
* @author Alexey Andreev
*/
public class TThread extends TObject implements TRunnable {
private static TThread currentThread = new TThread(TString.wrap("main"));
private TString name;
private TRunnable target;
public TThread() {
this(null, null);
}
public TThread(TString name) {
this(name, null);
}
public TThread(TRunnable target) {
this(null, target);
}
public TThread(TString name, TRunnable target) {
this.name = name;
this.target = target;
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
public static TThread currentThread() {
return currentThread;
}
public TString getName() {
return name;
}
public static void yield() {
}
public void interrupt() {
}
public static boolean interrupted() {
return false;
}
public boolean isInterrupted() {
return false;
}
public static int activeCount() {
return 1;
}
}

View File

@ -834,6 +834,8 @@ public class Renderer implements ExprVisitor, StatementVisitor {
} else { } else {
return "new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")"; return "new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")";
} }
} else if (cst instanceof Character) {
return Integer.toString((Character)cst);
} else { } else {
return cst.toString(); return cst.toString();
} }

View File

@ -86,6 +86,10 @@ public class JavaScriptBodyDependency implements DependencyListener {
dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, DependencyStack.ROOT).use(); dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, DependencyStack.ROOT).use();
dependencyChecker.linkMethod(JavaScriptConvGenerator.booleanValueMethod, DependencyStack.ROOT).use(); dependencyChecker.linkMethod(JavaScriptConvGenerator.booleanValueMethod, DependencyStack.ROOT).use();
dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, DependencyStack.ROOT).use(); dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, DependencyStack.ROOT).use();
dependencyChecker.linkMethod(JavaScriptConvGenerator.doubleValueMethod, DependencyStack.ROOT).use();
dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfDoubleMethod, DependencyStack.ROOT).use();
dependencyChecker.linkMethod(JavaScriptConvGenerator.charValueMethod, DependencyStack.ROOT).use();
dependencyChecker.linkMethod(JavaScriptConvGenerator.valueOfCharMethod, DependencyStack.ROOT).use();
} }
@Override @Override

View File

@ -84,8 +84,29 @@ public class JavaScriptBodyGenerator implements Generator {
@Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) {
MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); MethodDescriptor desc = MethodDescriptor.parse(method + params + "V");
MethodReader reader = findMethod(fqn, desc); MethodReader reader = findMethod(fqn, desc);
return ident == null ? naming.getFullNameFor(reader.getReference()) + "(" : StringBuilder sb = new StringBuilder();
ident + "." + naming.getNameFor(reader.getReference()) + "("; sb.append("(function($this");
for (int i = 0; i < reader.parameterCount(); ++i) {
sb.append(", ").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(reader.getReference()));
}
sb.append("(");
for (int i = 0; i < reader.parameterCount(); ++i) {
if (i > 0) {
sb.append(", ");
}
sb.append(naming.getFullNameFor(JavaScriptConvGenerator.fromJsMethod)).append("(p").append(i)
.append(", ")
.append(Renderer.typeToClsString(naming, reader.parameterType(i))).append(")");
}
sb.append(")); })(");
sb.append(ident == null ? "null, " : ident);
return sb.toString();
} }
private MethodReader findMethod(String clsName, MethodDescriptor desc) { private MethodReader findMethod(String clsName, MethodDescriptor desc) {
while (clsName != null) { while (clsName != null) {

View File

@ -31,10 +31,18 @@ public class JavaScriptConvGenerator implements Generator {
new MethodDescriptor("intValue", ValueType.INTEGER)); new MethodDescriptor("intValue", ValueType.INTEGER));
static final MethodReference booleanValueMethod = new MethodReference("java.lang.Boolean", static final MethodReference booleanValueMethod = new MethodReference("java.lang.Boolean",
new MethodDescriptor("booleanValue", ValueType.BOOLEAN)); new MethodDescriptor("booleanValue", ValueType.BOOLEAN));
static final MethodReference doubleValueMethod = new MethodReference("java.lang.Double",
new MethodDescriptor("doubleValue", ValueType.DOUBLE));
static final MethodReference charValueMethod = new MethodReference("java.lang.Character",
new MethodDescriptor("charValue", ValueType.CHARACTER));
static final MethodReference valueOfIntMethod = new MethodReference("java.lang.Integer", static final MethodReference valueOfIntMethod = new MethodReference("java.lang.Integer",
new MethodDescriptor("valueOf", ValueType.INTEGER, ValueType.object("java.lang.Integer"))); new MethodDescriptor("valueOf", ValueType.INTEGER, ValueType.object("java.lang.Integer")));
static final MethodReference valueOfBooleanMethod = new MethodReference("java.lang.Boolean", static final MethodReference valueOfBooleanMethod = new MethodReference("java.lang.Boolean",
new MethodDescriptor("valueOf", ValueType.BOOLEAN, ValueType.object("java.lang.Boolean"))); new MethodDescriptor("valueOf", ValueType.BOOLEAN, ValueType.object("java.lang.Boolean")));
static final MethodReference valueOfDoubleMethod = new MethodReference("java.lang.Double",
new MethodDescriptor("valueOf", ValueType.DOUBLE, ValueType.object("java.lang.Double")));
static final MethodReference valueOfCharMethod = new MethodReference("java.lang.Character",
new MethodDescriptor("valueOf", ValueType.CHARACTER, ValueType.object("java.lang.Character")));
private static final ValueType objType = ValueType.object("java.lang.Object"); private static final ValueType objType = ValueType.object("java.lang.Object");
static final MethodReference toJsMethod = new MethodReference(convCls, new MethodDescriptor( static final MethodReference toJsMethod = new MethodReference(convCls, new MethodDescriptor(
"toJavaScript", objType, objType)); "toJavaScript", objType, objType));
@ -55,11 +63,12 @@ public class JavaScriptConvGenerator implements Generator {
private void generateToJavaScript(GeneratorContext context, SourceWriter writer) throws IOException { private void generateToJavaScript(GeneratorContext context, SourceWriter writer) throws IOException {
String obj = context.getParameterName(1); String obj = context.getParameterName(1);
writer.append("if (" + obj + " === null) {").softNewLine().indent(); writer.append("if (" + obj + " === null || " + obj + " === undefined) {").softNewLine().indent();
writer.append("return null;").softNewLine(); writer.append("return " + obj + ";").softNewLine();
writer.outdent().append("} else if (typeof " + obj + " === 'number') {").indent().softNewLine(); writer.outdent().append("} else if (typeof " + obj + " === 'number') {").indent().softNewLine();
writer.append("return " + obj + ";").softNewLine(); writer.append("return " + obj + ";").softNewLine();
writer.outdent().append("} else if (" + obj + ".constructor.$meta.item) {").indent().softNewLine(); writer.outdent().append("} else if (" + obj + ".constructor.$meta && " + obj + ".constructor.$meta.item) {")
.indent().softNewLine();
writer.append("var arr = new Array(" + obj + ".data.length);").softNewLine(); writer.append("var arr = new Array(" + obj + ".data.length);").softNewLine();
writer.append("for (var i = 0; i < arr.length; ++i) {").indent().softNewLine(); writer.append("for (var i = 0; i < arr.length; ++i) {").indent().softNewLine();
writer.append("arr[i] = ").appendMethodBody(toJsMethod).append("(" + obj + ".data[i]);").softNewLine(); writer.append("arr[i] = ").appendMethodBody(toJsMethod).append("(" + obj + ".data[i]);").softNewLine();
@ -70,10 +79,16 @@ public class JavaScriptConvGenerator implements Generator {
generateStringToJavaScript(context, writer); generateStringToJavaScript(context, writer);
writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Integer") writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Integer")
.append(") {").indent().softNewLine(); .append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(intValueMethod).append("(" + obj + ");").softNewLine(); writer.append("return ").appendMethodBody(intValueMethod).append("(" + obj + ")|0;").softNewLine();
writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Boolean") writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Boolean")
.append(") {").indent().softNewLine(); .append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(booleanValueMethod).append("(" + obj + ")!==0;").softNewLine(); writer.append("return ").appendMethodBody(booleanValueMethod).append("(" + obj + ")!==0;").softNewLine();
writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Double")
.append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(doubleValueMethod).append("(" + obj + ");").softNewLine();
writer.outdent().append("} else if (" + obj + ".constructor === ").appendClass("java.lang.Character")
.append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(charValueMethod).append("(" + obj + ");").softNewLine();
writer.outdent().append("} else {").indent().softNewLine(); writer.outdent().append("} else {").indent().softNewLine();
writer.append("return " + obj + ";").softNewLine(); writer.append("return " + obj + ";").softNewLine();
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
@ -84,12 +99,12 @@ public class JavaScriptConvGenerator implements Generator {
String type = context.getParameterName(2); String type = context.getParameterName(2);
writer.append("if (" + obj +" === null || " + obj + " === undefined)").ws().append("{") writer.append("if (" + obj +" === null || " + obj + " === undefined)").ws().append("{")
.softNewLine().indent(); .softNewLine().indent();
writer.append("return null;").softNewLine(); writer.append("return " + obj +";").softNewLine();
writer.outdent().append("} else if (" + type + ".$meta.item) {").indent().softNewLine(); writer.outdent().append("} else if (" + type + ".$meta.item) {").indent().softNewLine();
writer.append("var arr = $rt_createArray(" + type + ".$meta.item, " + obj + ".length);").softNewLine(); writer.append("var arr = $rt_createArray(" + type + ".$meta.item, " + obj + ".length);").softNewLine();
writer.append("for (var i = 0; i < arr.data.length; ++i) {").indent().softNewLine(); writer.append("for (var i = 0; i < arr.data.length; ++i) {").indent().softNewLine();
writer.append("arr.data[i] = ").appendMethodBody(fromJsMethod).append("(" + obj + "[i], " + type + ");") writer.append("arr.data[i] = ").appendMethodBody(fromJsMethod).append("(" + obj + "[i], " +
.softNewLine(); type + ".$meta.item);").softNewLine();
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.append("return arr;").softNewLine(); writer.append("return arr;").softNewLine();
writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.String") writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.String")
@ -98,11 +113,18 @@ public class JavaScriptConvGenerator implements Generator {
writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.Integer") writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.Integer")
.append(") {").indent().softNewLine(); .append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfIntMethod).append("(" + obj + ");").softNewLine(); writer.append("return ").appendMethodBody(valueOfIntMethod).append("(" + obj + ");").softNewLine();
writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.Double")
.append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfDoubleMethod).append("(" + obj + ");").softNewLine();
writer.outdent().append("} else if (" + type + " === $rt_intcls()) {").indent().softNewLine(); writer.outdent().append("} else if (" + type + " === $rt_intcls()) {").indent().softNewLine();
writer.append("return " + obj + "|0;").softNewLine(); writer.append("return " + obj + "|0;").softNewLine();
writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.Boolean") writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.Boolean")
.append(") {").indent().softNewLine(); .append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfBooleanMethod).append("(" + obj + "?1:0);").softNewLine(); writer.append("return ").appendMethodBody(valueOfBooleanMethod).append("(" + obj + "?1:0);").softNewLine();
writer.outdent().append("} else if (" + type + " === ").appendClass("java.lang.Character")
.append(") {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfCharMethod).append("(typeof " + obj + " === 'number' ? " +
obj + "0xFFFF : " + obj + ".charCodeAt(0));").softNewLine();
writer.outdent().append("} else if (" + type + " === $rt_booleancls()) {").indent().softNewLine(); writer.outdent().append("} else if (" + type + " === $rt_booleancls()) {").indent().softNewLine();
writer.append("return " + obj + "?1:0;").softNewLine(); writer.append("return " + obj + "?1:0;").softNewLine();
writer.outdent().append("} else if (" + obj + " instanceof Array) {").indent().softNewLine(); writer.outdent().append("} else if (" + obj + " instanceof Array) {").indent().softNewLine();
@ -112,9 +134,13 @@ public class JavaScriptConvGenerator implements Generator {
.softNewLine(); .softNewLine();
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.append("return arr;").softNewLine(); writer.append("return arr;").softNewLine();
writer.outdent().append("} else if (typeof " + obj + " === 'string') {").indent().softNewLine();
writer.append("return $rt_str(" + obj + ");").softNewLine();
writer.outdent().append("} else if (typeof " + obj + " === 'number') {").indent().softNewLine(); writer.outdent().append("} else if (typeof " + obj + " === 'number') {").indent().softNewLine();
writer.append("if (" + obj + "|0 === " + obj + ") {").indent().softNewLine(); writer.append("if (" + obj + "|0 === " + obj + ") {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfIntMethod).append("(" + obj + ");").softNewLine(); writer.append("return ").appendMethodBody(valueOfIntMethod).append("(" + obj + ");").softNewLine();
writer.outdent().append("} else {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfDoubleMethod).append("(" + obj + ");").softNewLine();
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.outdent().append("} else if (typeof " + obj + " === 'boolean') {").indent().softNewLine(); writer.outdent().append("} else if (typeof " + obj + " === 'boolean') {").indent().softNewLine();
writer.append("return ").appendMethodBody(valueOfBooleanMethod).append("(" + obj + "?1:0);").softNewLine(); writer.append("return ").appendMethodBody(valueOfBooleanMethod).append("(" + obj + "?1:0);").softNewLine();

View File

@ -93,14 +93,14 @@ public class JavaScriptBodyTests {
assertEquals(1, run.cnt); assertEquals(1, run.cnt);
} }
/*@Test public void typeOfCharacter() { @Test public void typeOfCharacter() {
String charType = Bodies.typeof('a', false); String charType = Bodies.typeof('a', false);
assertEquals("number", charType); assertEquals("number", charType);
}*/ }
@Test public void typeOfBoolean() { @Test public void typeOfBoolean() {
String booleanType = Bodies.typeof(true, false); String booleanType = Bodies.typeof(true, false);
assertEquals("boolean", equals(booleanType)); assertEquals("boolean", booleanType);
} }
@Test public void typeOfPrimitiveBoolean() { @Test public void typeOfPrimitiveBoolean() {
@ -110,7 +110,7 @@ public class JavaScriptBodyTests {
@Test public void typeOfInteger() { @Test public void typeOfInteger() {
String intType = Bodies.typeof(1, false); String intType = Bodies.typeof(1, false);
assertEquals("number", equals(intType)); assertEquals("number", intType);
} }
@Test public void typeOfString() { @Test public void typeOfString() {
@ -118,10 +118,10 @@ public class JavaScriptBodyTests {
assertEquals("string", strType); assertEquals("string", strType);
} }
/*@Test public void typeOfDouble() { @Test public void typeOfDouble() {
String doubleType = Bodies.typeof(0.33, false); String doubleType = Bodies.typeof(0.33, false);
assertEquals("number", doubleType); assertEquals("number", doubleType);
}*/ }
@Test public void typeOfBooleanValueOf() { @Test public void typeOfBooleanValueOf() {
String booleanType = Bodies.typeof(true, true); String booleanType = Bodies.typeof(true, true);
@ -138,10 +138,10 @@ public class JavaScriptBodyTests {
assertEquals("string", strType); assertEquals("string", strType);
} }
/*@Test public void typeOfDoubleValueOf() { @Test public void typeOfDoubleValueOf() {
String doubleType = Bodies.typeof(0.33, true); String doubleType = Bodies.typeof(0.33, true);
assertEquals("number", doubleType); assertEquals("number", doubleType);
}*/ }
@Test public void computeInARunnable() { @Test public void computeInARunnable() {
final int[] sum = new int[2]; final int[] sum = new int[2];
@ -156,7 +156,7 @@ public class JavaScriptBodyTests {
assertEquals(42, sum[1]); assertEquals(42, sum[1]);
} }
/*@Test public void doubleCallbackToRunnable() { @Test public void doubleCallbackToRunnable() {
final R run = new R(); final R run = new R();
final R r2 = new R(); final R r2 = new R();
class First implements Runnable { class First implements Runnable {
@ -313,7 +313,7 @@ public class JavaScriptBodyTests {
Thread.sleep(50); Thread.sleep(50);
} }
assert l.call == 42 : "Method was called: " + l.call; assert l.call == 42 : "Method was called: " + l.call;
}*/ }
private static class R implements Runnable { private static class R implements Runnable {
int cnt; int cnt;