diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TInputStreamReader.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TInputStreamReader.java index 2a39581aa..9ea5ff73d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TInputStreamReader.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TInputStreamReader.java @@ -89,18 +89,20 @@ public class TInputStreamReader extends TReader { if (eof) { return false; } - int i = 0; - while (!outBuffer.end()) { - outData[i++] = outBuffer.get(); - } - outBuffer.rewind(i); - while (outBuffer.available() > 4) { - if (inBuffer.available() < 8 && !fillReadBuffer()) { + CharBuffer newBuffer = new CharBuffer(outData); + newBuffer.put(outBuffer); + while (true) { + if (inBuffer.end() && !fillReadBuffer()) { eof = true; break; } - charset.decode(inBuffer, outBuffer); + int oldAvail = newBuffer.available(); + charset.decode(inBuffer, newBuffer); + if (oldAvail == newBuffer.available()) { + break; + } } + outBuffer = new CharBuffer(outData, 0, newBuffer.position()); return true; } @@ -121,8 +123,12 @@ public class TInputStreamReader extends TReader { break; } else { off += bytesRead; + if (bytesRead == 0) { + break; + } } } + inBuffer = new ByteBuffer(inData, 0, off); return true; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java new file mode 100644 index 000000000..0bc454b27 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java @@ -0,0 +1,32 @@ +/* + * 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 TArrayIndexOutOfBoundsException extends TIndexOutOfBoundsException { + private static final long serialVersionUID = 5221353436321708950L; + + public TArrayIndexOutOfBoundsException() { + super(); + } + + public TArrayIndexOutOfBoundsException(TString message) { + super(message); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index 4a35810e8..7feda0d2e 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -32,6 +32,10 @@ import org.teavm.model.ValueType; * @author Alexey Andreev */ public class ArrayNativeGenerator implements Generator, DependencyPlugin { + private static final MethodReference valueOfIntMethod = new MethodReference("java.lang.Integer", + "valueOf", ValueType.INTEGER, ValueType.object("java.lang.Integer")); + private static final MethodReference valueOfCharMethod = new MethodReference("java.lang.Character", + "valueOf", ValueType.CHARACTER, ValueType.object("java.lang.Character")); private static final String[] primitives = { "Byte", "Short", "Char", "Int", "Long", "Float", "Double", "Boolean" }; @@ -44,6 +48,9 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { case "newInstanceImpl": method.getResult().propagate("[java.lang.Object"); break; + case "getImpl": + achieveGet(checker, method); + break; } } @@ -56,6 +63,9 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { case "newInstanceImpl": generateNewInstance(context, writer); break; + case "getImpl": + generateGet(context, writer); + break; } } @@ -96,4 +106,29 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { writer.append("return $rt_createArray(cls, " + length + ")").softNewLine(); writer.outdent().append("}").softNewLine(); } + + private void generateGet(GeneratorContext context, SourceWriter writer) throws IOException { + String array = context.getParameterName(1); + writer.append("var item = " + array + ".data[" + context.getParameterName(2) + "];").softNewLine(); + writer.append("var type = " + array + ".constructor.$meta.item;").softNewLine(); + writer.append("if (type === $rt_intcls()) {").indent().softNewLine(); + writer.append("item = ").appendMethodBody(valueOfIntMethod).append("(item);").softNewLine(); + writer.outdent().append("} else if (type === $rt_charcls()) {").indent().softNewLine(); + writer.append("item = ").appendMethodBody(valueOfCharMethod).append("(item);").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return item;").softNewLine(); + } + + private void achieveGet(final DependencyChecker checker, final MethodDependency method) { + method.getVariable(1).getArrayItem().connect(method.getResult()); + method.getVariable(1).addConsumer(new DependencyConsumer() { + @Override public void consume(String type) { + if (type.equals("[I")) { + checker.linkMethod(valueOfIntMethod, method.getStack()).use(); + } else if (type.equals("[C")) { + checker.linkMethod(valueOfCharMethod, method.getStack()).use(); + } + } + }); + } } 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 index 2f3c283e8..e7dbff4ee 100644 --- 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 @@ -44,4 +44,16 @@ public final class TArray extends TObject { @GeneratedBy(ArrayNativeGenerator.class) @PluggableDependency(ArrayNativeGenerator.class) private static native TObject newInstanceImpl(TClass componentType, int length); + + public static TObject get(TObject array, int index) throws TIllegalArgumentException, + TArrayIndexOutOfBoundsException { + if (index < 0 || index >= getLength(array)) { + throw new TArrayIndexOutOfBoundsException(); + } + return getImpl(array, index); + } + + @GeneratedBy(ArrayNativeGenerator.class) + @PluggableDependency(ArrayNativeGenerator.class) + private static native TObject getImpl(TObject array, int index); } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/io/InputStreamReaderTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/io/InputStreamReaderTest.java new file mode 100644 index 000000000..b50cdee4d --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/io/InputStreamReaderTest.java @@ -0,0 +1,67 @@ +/* + * 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.io; + +import static org.junit.Assert.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class InputStreamReaderTest { + @Test + public void readsChars() throws IOException { + String str = "foo bar baz"; + byte[] bytes = new byte[str.length()]; + for (int i = 0; i < str.length(); ++i) { + bytes[i] = (byte)str.charAt(i); + } + ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); + char[] chars = new char[100]; + int readChars = reader.read(chars); + assertEquals(str.length(), readChars); + for (int i = 0; i < str.length(); ++i) { + assertEquals(str.charAt(i), chars[i]); + } + } + + @Test + public void readsManyChars() throws IOException { + StringBuilder sb = new StringBuilder(); + String str = "foo bar baz"; + for (int i = 0; i < 10000; ++i) { + sb.append(str); + } + str = sb.toString(); + byte[] bytes = new byte[str.length()]; + for (int i = 0; i < str.length(); ++i) { + bytes[i] = (byte)str.charAt(i); + } + ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); + char[] chars = new char[12000]; + int readChars = reader.read(chars); + assertEquals(chars.length, readChars); + for (int i = 0; i < chars.length; ++i) { + assertEquals(str.charAt(i), chars[i]); + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java index bc1692335..f3a22dff2 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java @@ -17,7 +17,9 @@ package org.teavm.codegen; import java.io.IOException; import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; /** * @@ -86,10 +88,20 @@ public class SourceWriter implements Appendable { return append(naming.getNameFor(method)); } + public SourceWriter appendMethod(String className, String name, ValueType... params) + throws NamingException, IOException { + return append(naming.getNameFor(new MethodReference(className, new MethodDescriptor(name, params)))); + } + public SourceWriter appendMethodBody(MethodReference method) throws NamingException, IOException { return append(naming.getFullNameFor(method)); } + public SourceWriter appendMethodBody(String className, String name, ValueType... params) + throws NamingException, IOException { + return append(naming.getFullNameFor(new MethodReference(className, new MethodDescriptor(name, params)))); + } + private void appendIndent() throws IOException { if (minified) { return; diff --git a/teavm-core/src/main/java/org/teavm/model/MethodReference.java b/teavm-core/src/main/java/org/teavm/model/MethodReference.java index e70bdea63..154703e40 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodReference.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodReference.java @@ -28,6 +28,10 @@ public class MethodReference { this.descriptor = descriptor; } + public MethodReference(String className, String name, ValueType... signature) { + this(className, new MethodDescriptor(name, signature)); + } + public String getClassName() { return className; }