diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index 7f09e716e..2aa1324db 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -33,6 +33,9 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { case "clone": generateClone(context, writer); break; + case "wrap": + generateWrap(context, writer); + break; } } @@ -45,6 +48,9 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { case "getClass": achieveGetClass(checker); break; + case "wrap": + achieveWrap(checker, method); + break; } } @@ -96,4 +102,13 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { MethodGraph graph = checker.attachMethodGraph(method); graph.getVariableNode(0).connect(graph.getResultNode()); } + + private void generateWrap(GeneratorContext context, SourceWriter writer) { + writer.append("return ").append(context.getParameterName(1)); + } + + private void achieveWrap(DependencyChecker checker, MethodReference method) { + MethodGraph graph = checker.attachMethodGraph(method); + graph.getVariableNode(1).connect(graph.getResultNode()); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TNullPointerException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TNullPointerException.java new file mode 100644 index 000000000..0f933ed38 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TNullPointerException.java @@ -0,0 +1,17 @@ +package org.teavm.classlib.java.lang; + +/** + * + * @author Alexey Andreev + */ +public class TNullPointerException extends TRuntimeException { + private static final long serialVersionUID = 2639861320773057190L; + + public TNullPointerException(TString message) { + super(message); + } + + public TNullPointerException() { + super(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 935cde39b..4caf3ea0d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -65,4 +65,8 @@ public class TObject { @Override protected void finalize() throws TThrowable { } + + @GeneratedBy(ObjectNativeGenerator.class) + @PluggableDependency(ObjectNativeGenerator.class) + public static native TObject wrap(Object obj); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index 2c1d3a335..dffc22154 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang; +import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.javascript.ni.GeneratedBy; /** @@ -10,7 +11,17 @@ public final class TSystem extends TObject { private TSystem() { } - public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) { + public static void arraycopy(TObject src, int srcPos, TObject dest, int destPos, int length) { + if (src == null || dest == null) { + throw new TNullPointerException(TString.wrap("Either src or dest is null")); + } + if (src.getClass0() != dest.getClass0()) { + throw new ArrayStoreException(); + } + if (srcPos < 0 || destPos < 0 || length < 0 || srcPos + length > TArray.getLength(src) || + destPos + length > TArray.getLength(dest)) { + throw new IndexOutOfBoundsException(); + } doArrayCopy(src, srcPos, dest, destPos, length); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystemTests.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystemTests.java index e40414a47..83083a36a 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystemTests.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TSystemTests.java @@ -14,9 +14,19 @@ class TSystemTests { TObject b = new TObject(); TObject[] src = { a, b, a }; TObject[] dest = new TObject[3]; - TSystem.arraycopy(src, 0, dest, 0, 3); + TSystem.arraycopy(TObject.wrap(src), 0, TObject.wrap(dest), 0, 3); assertSame(a, dest[0]); assertSame(b, dest[1]); assertSame(a, dest[2]); } + + @Test + public void failsToCopyArraysWithInvalidIndexes() { + TSystem.arraycopy(TObject.wrap(new TObject[0]), 0, TObject.wrap(new TObject[0]), 0, 1); + } + + @Test + public void failsToCopyArraysWithIncompatibleElements() { + TSystem.arraycopy(TObject.wrap(new TObject[1]), 0, TObject.wrap(new int[1]), 0, 1); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java new file mode 100644 index 000000000..86fd2ffb2 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java @@ -0,0 +1,16 @@ +package org.teavm.classlib.java.lang.reflect; + +import org.teavm.classlib.java.lang.TIllegalArgumentException; +import org.teavm.classlib.java.lang.TObject; +import org.teavm.dependency.PluggableDependency; +import org.teavm.javascript.ni.GeneratedBy; + +/** + * + * @author Alexey Andreev + */ +public final class TArray extends TObject { + @GeneratedBy(TArrayNativeGenerator.class) + @PluggableDependency(TArrayNativeGenerator.class) + public static native int getLength(TObject array) throws TIllegalArgumentException; +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArrayNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArrayNativeGenerator.java new file mode 100644 index 000000000..2c808980a --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArrayNativeGenerator.java @@ -0,0 +1,58 @@ +package org.teavm.classlib.java.lang.reflect; + +import org.teavm.codegen.SourceWriter; +import org.teavm.dependency.DependencyChecker; +import org.teavm.dependency.DependencyConsumer; +import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodGraph; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +/** + * + * @author Alexey Andreev + */ +public class TArrayNativeGenerator implements Generator, DependencyPlugin { + @Override + public void methodAchieved(DependencyChecker checker, MethodReference method) { + if (method.getName().equals("getLength")) { + achieveGetLength(checker, method); + } + } + + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + switch (methodRef.getName()) { + case "getLength": + generateGetLength(context, writer); + break; + } + } + + private void generateGetLength(GeneratorContext context, SourceWriter writer) { + String array = context.getParameterName(1); + writer.append("if (" + array + " === null || " + array + " .$cls.$meta.item === undefined) {") + .newLine().indent(); + String clsName = "java.lang.IllegalArgumentException"; + MethodReference cons = new MethodReference(clsName, new MethodDescriptor("", ValueType.VOID)); + writer.append("$rt_throw(").appendClass(clsName).append(".").appendMethod(cons).append("());").newLine(); + writer.outdent().append("}").newLine(); + writer.append("return array.length"); + } + + private void achieveGetLength(final DependencyChecker checker, MethodReference methodRef) { + final MethodGraph graph = checker.attachMethodGraph(methodRef); + graph.getVariableNode(1).addConsumer(new DependencyConsumer() { + @Override public void consume(String type) { + if (!type.startsWith("[")) { + MethodReference cons = new MethodReference("java.lang.IllegalArgumentException", + new MethodDescriptor("", ValueType.VOID)); + checker.addEntryPoint(cons); + } + } + }); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java index ac3d01776..53833f742 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -66,9 +66,12 @@ public class ClasslibTestGenerator { out.println(IOUtils.toString(input)); } renderer.renderRuntime(); + writer.append("runTests = function() {").newLine().indent(); + writer.append("document.getElementById(\"start-button\").style.display = 'none';").newLine(); for (String testClass : testClasses) { renderClassTest(classSource.getClassHolder(testClass)); } + writer.outdent().append("}").newLine(); out.println(writer); renderFoot(); } @@ -87,6 +90,16 @@ public class ClasslibTestGenerator { out.println(" TeaVM JUnit tests"); out.println(" "); out.println(" TeaVM JUnit tests"); + out.println(" "); out.println(" "); out.println(" "); out.println(" "); + out.println(" "); out.println(" "); out.println(""); } diff --git a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java index af4f2c92a..f5106b956 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -1246,6 +1246,7 @@ public class ProgramParser { ArrayLengthInstruction insn = new ArrayLengthInstruction(); insn.setArray(getVariable(a)); insn.setReceiver(getVariable(a)); + builder.add(insn); break; } case Opcodes.ATHROW: { diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index d311efe86..30ec320b7 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -157,6 +157,11 @@ $rt_init = function(cls, constructor, args) { cls.prototype[constructor].apply(obj, args); return obj; } +$rt_throw = function(ex) { + var err = new Error("Java exception thrown"); + err.$javaException = ex; + throw err; +} $rt = { createBooleanArray : function(cls, sz) {