Add support for several JDK classes and functions

This commit is contained in:
Alexey Andreev 2017-11-26 17:10:56 +03:00
parent cc0f7583b0
commit cc04c3446d
7 changed files with 693 additions and 0 deletions

View File

@ -0,0 +1,145 @@
/*
* Copyright 2017 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.io;
import java.io.IOException;
import java.io.Reader;
public class TCharArrayReader extends Reader {
protected char[] buf;
protected int pos;
protected int markedPos = -1;
protected int count;
public TCharArrayReader(char[] buf) {
this.buf = buf;
this.count = buf.length;
}
public TCharArrayReader(char[] buf, int offset, int length) {
/*
* The spec of this constructor is broken. In defining the legal values
* of offset and length, it doesn't consider buffer's length. And to be
* compatible with the broken spec, we must also test whether
* (offset + length) overflows.
*/
if (offset < 0 || offset > buf.length || length < 0 || offset + length < 0) {
throw new IllegalArgumentException();
}
this.buf = buf;
this.pos = offset;
this.markedPos = offset;
/* This is according to spec */
int bufferLength = buf.length;
this.count = offset + length < bufferLength ? length : bufferLength;
}
@Override
public void close() {
if (isOpen()) {
buf = null;
}
}
private boolean isOpen() {
return buf != null;
}
private boolean isClosed() {
return buf == null;
}
@Override
public void mark(int readLimit) throws IOException {
if (isClosed()) {
throw new IOException();
}
markedPos = pos;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public int read() throws IOException {
if (isClosed()) {
throw new IOException();
}
if (pos == count) {
return -1;
}
return buf[pos++];
}
@Override
public int read(char[] buffer, int offset, int len) throws IOException {
if (offset < 0 || offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException("Offset out of bounds:" + offset);
}
if (len < 0 || len > buffer.length - offset) {
throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + len);
}
if (isClosed()) {
throw new IOException();
}
if (pos < this.count) {
int bytesRead = pos + len > this.count ? this.count - pos : len;
System.arraycopy(this.buf, pos, buffer, offset, bytesRead);
pos += bytesRead;
return bytesRead;
}
return -1;
}
@Override
public boolean ready() throws IOException {
if (isClosed()) {
throw new IOException();
}
return pos != count;
}
@Override
public void reset() throws IOException {
if (isClosed()) {
throw new IOException();
}
pos = markedPos != -1 ? markedPos : 0;
}
@Override
public long skip(long n) throws IOException {
if (isClosed()) {
throw new IOException();
}
if (n <= 0) {
return 0;
}
long skipped;
if (n < this.count - pos) {
pos = pos + (int) n;
skipped = n;
} else {
skipped = this.count - pos;
pos = this.count;
}
return skipped;
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright 2017 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.io;
import java.io.IOException;
import java.io.Writer;
public class TCharArrayWriter extends Writer {
protected char[] buf;
protected int count;
public TCharArrayWriter() {
buf = new char[32];
lock = buf;
}
public TCharArrayWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException();
}
buf = new char[initialSize];
lock = buf;
}
@Override
public void close() {
/* empty */
}
private void expand(int i) {
/* Can the buffer handle @i more chars, if not expand it */
if (count + i <= buf.length) {
return;
}
int newLen = Math.max(2 * buf.length, count + i);
char[] newbuf = new char[newLen];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
@Override
public void flush() {
/* empty */
}
public void reset() {
count = 0;
}
public int size() {
return count;
}
public char[] toCharArray() {
char[] result = new char[count];
System.arraycopy(buf, 0, result, 0, count);
return result;
}
@Override
public String toString() {
return new String(buf, 0, count);
}
@Override
public void write(char[] c, int offset, int len) {
// avoid int overflow
if (offset < 0 || offset > c.length || len < 0 || len > c.length - offset) {
throw new IndexOutOfBoundsException();
}
expand(len);
System.arraycopy(c, offset, this.buf, this.count, len);
this.count += len;
}
@Override
public void write(int oneChar) {
expand(1);
buf[count++] = (char) oneChar;
}
@Override
public void write(String str, int offset, int len) {
if (str == null) {
throw new NullPointerException();
}
// avoid int overflow
if (offset < 0 || offset > str.length() || len < 0 || len > str.length() - offset) {
throw new StringIndexOutOfBoundsException();
}
expand(len);
str.getChars(offset, offset + len, buf, this.count);
this.count += len;
}
public void writeTo(Writer out) throws IOException {
out.write(buf, 0, count);
}
@Override
public TCharArrayWriter append(char c) {
write(c);
return this;
}
@Override
public TCharArrayWriter append(CharSequence csq) {
if (null == csq) {
append("NULL", 0, 4);
} else {
append(csq, 0, csq.length());
}
return this;
}
@Override
public TCharArrayWriter append(CharSequence csq, int start, int end) {
if (null == csq) {
csq = "NULL";
}
String output = csq.subSequence(start, end).toString();
write(output, 0, output.length());
return this;
}
}

View File

@ -625,4 +625,10 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
return getClassLoader().getResourceAsStream(name); return getClassLoader().getResourceAsStream(name);
} }
public TPackage getPackage() {
String name = (String) (Object) getName();
name = name.substring(0, name.lastIndexOf('.') + 1);
return TPackage.getPackage(name);
}
} }

View File

@ -0,0 +1,58 @@
/*
* Copyright 2017 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;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.HashMap;
import java.util.Map;
public class TPackage implements AnnotatedElement {
private static Map<String, TPackage> packages = new HashMap<>();
private String name;
TPackage(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static TPackage getPackage(String name) {
TPackage pkg = packages.get(name);
if (pkg == null) {
pkg = new TPackage(name);
packages.put(name, pkg);
}
return pkg;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return null;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public Annotation[] getDeclaredAnnotations() {
return new Annotation[0];
}
}

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.io.TPrintStream;
import org.teavm.classlib.java.io.TPrintWriter;
import org.teavm.classlib.java.util.TArrays; import org.teavm.classlib.java.util.TArrays;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.interop.Remove; import org.teavm.interop.Remove;
@ -166,6 +167,20 @@ public class TThrowable extends RuntimeException {
} }
} }
public void printStackTrace(TPrintWriter stream) {
stream.println(TString.wrap(getClass().getName() + ": " + getMessage()));
if (stackTrace != null) {
for (TStackTraceElement element : stackTrace) {
stream.print(TString.wrap(" at "));
stream.println(element);
}
}
if (cause != null && cause != this) {
stream.print(TString.wrap("Caused by: "));
cause.printStackTrace(stream);
}
}
@Rename("getStackTrace") @Rename("getStackTrace")
public TStackTraceElement[] getStackTrace0() { public TStackTraceElement[] getStackTrace0() {
return stackTrace != null ? stackTrace.clone() : new TStackTraceElement[0]; return stackTrace != null ? stackTrace.clone() : new TStackTraceElement[0];

View File

@ -0,0 +1,150 @@
/*
* Copyright 2017 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.io;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.CharArrayReader;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class CharArrayReaderTest {
char[] hw = { 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd' };
CharArrayReader cr;
@Test
public void constructor$C() throws IOException {
cr = new CharArrayReader(hw);
assertTrue("Failed to create reader", cr.ready());
}
@Test
public void constructor$CII() throws IOException {
cr = new CharArrayReader(hw, 5, 5);
assertTrue("Failed to create reader", cr.ready());
int c = cr.read();
assertTrue("Created incorrect reader--returned '" + (char) c + "' instead of 'W'", c == 'W');
}
@Test
public void close() {
cr = new CharArrayReader(hw);
cr.close();
try {
cr.read();
fail("Failed to throw exception on read from closed stream");
} catch (IOException e) {
// Expected
}
// No-op
cr.close();
}
@Test
public void markI() throws IOException {
cr = new CharArrayReader(hw);
cr.skip(5L);
cr.mark(100);
cr.read();
cr.reset();
assertEquals("Failed to mark correct position", 'W', cr.read());
}
@Test
public void markSupported() {
cr = new CharArrayReader(hw);
assertTrue("markSupported returned false", cr.markSupported());
}
@Test
public void read() throws IOException {
cr = new CharArrayReader(hw);
assertEquals("Read returned incorrect char", 'H', cr.read());
cr = new CharArrayReader(new char[] { '\u8765' });
assertTrue("Incorrect double byte char", cr.read() == '\u8765');
}
@Test
public void read$CII() throws IOException {
char[] c = new char[11];
cr = new CharArrayReader(hw);
cr.read(c, 1, 10);
assertTrue("Read returned incorrect chars", new String(c, 1, 10).equals(new String(hw, 0, 10)));
}
@Test
public void ready() throws IOException {
cr = new CharArrayReader(hw);
assertTrue("ready returned false", cr.ready());
cr.skip(1000);
assertTrue("ready returned true", !cr.ready());
cr.close();
try {
cr.ready();
fail("No exception 1");
} catch (IOException e) {
// expected
}
try {
cr = new CharArrayReader(hw);
cr.close();
cr.ready();
fail("No exception 2");
} catch (IOException e) {
// expected
}
}
@Test
public void reset() throws IOException {
cr = new CharArrayReader(hw);
cr.skip(5L);
cr.mark(100);
cr.read();
cr.reset();
assertEquals("Reset failed to return to marker position", 'W', cr.read());
// Regression for HARMONY-4357
String str = "offsetHello world!";
char[] data = new char[str.length()];
str.getChars(0, str.length(), data, 0);
int offsetLength = 6;
int length = data.length - offsetLength;
CharArrayReader reader = new CharArrayReader(data, offsetLength, length);
reader.reset();
for (int i = 0; i < length; i++) {
assertEquals(data[offsetLength + i], (char) reader.read());
}
}
@Test
public void skipJ() throws IOException {
cr = new CharArrayReader(hw);
long skipped = cr.skip(5L);
assertEquals("Failed to skip correct number of chars", 5L, skipped);
assertEquals("Skip skipped wrong chars", 'W', cr.read());
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright 2017 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.io;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.StringWriter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class CharArrayWriterTest {
char[] hw = { 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd' };
CharArrayWriter cw = new CharArrayWriter();
CharArrayReader cr;
@Test
public void constructor() {
cw = new CharArrayWriter(90);
assertEquals("Created incorrect writer", 0, cw.size());
}
@Test
public void constructorI() {
cw = new CharArrayWriter();
assertEquals("Created incorrect writer", 0, cw.size());
}
@Test
public void close() {
cw.close();
}
@Test
public void flush() {
cw.flush();
}
@Test
public void reset() throws IOException {
cw.write("HelloWorld", 5, 5);
cw.reset();
cw.write("HelloWorld", 0, 5);
cr = new CharArrayReader(cw.toCharArray());
char[] c = new char[100];
cr.read(c, 0, 5);
assertEquals("Reset failed to reset buffer", "Hello", new String(c, 0, 5));
}
@Test
public void size() {
assertEquals("Returned incorrect size", 0, cw.size());
cw.write(hw, 5, 5);
assertEquals("Returned incorrect size", 5, cw.size());
}
@Test
public void toCharArray() throws IOException {
cw.write("HelloWorld", 0, 10);
cr = new CharArrayReader(cw.toCharArray());
char[] c = new char[100];
cr.read(c, 0, 10);
assertEquals("toCharArray failed to return correct array", "HelloWorld", new String(c, 0, 10));
}
@Test
public void test_toString() {
cw.write("HelloWorld", 5, 5);
cr = new CharArrayReader(cw.toCharArray());
assertEquals("Returned incorrect string", "World", cw.toString());
}
@Test
public void write$CII() throws IOException {
cw.write(hw, 5, 5);
cr = new CharArrayReader(cw.toCharArray());
char[] c = new char[100];
cr.read(c, 0, 5);
assertEquals("Writer failed to write correct chars", "World", new String(c, 0, 5));
}
@Test
public void write$CII_2() {
// Regression for HARMONY-387
CharArrayWriter obj = new CharArrayWriter();
try {
obj.write(new char[] { '0' }, 0, -1);
fail("IndexOutOfBoundsException expected");
} catch (IndexOutOfBoundsException t) {
assertEquals(
"IndexOutOfBoundsException rather than a subclass expected",
IndexOutOfBoundsException.class, t.getClass());
}
}
@Test
public void writeI() throws IOException {
cw.write('T');
cr = new CharArrayReader(cw.toCharArray());
assertEquals("Writer failed to write char", 'T', cr.read());
}
@Test
public void writeLjava_lang_StringII() throws IOException {
cw.write("HelloWorld", 5, 5);
cr = new CharArrayReader(cw.toCharArray());
char[] c = new char[100];
cr.read(c, 0, 5);
assertEquals("Writer failed to write correct chars", "World", new String(c, 0, 5));
}
@Test
public void writeLjava_lang_StringII_2() throws StringIndexOutOfBoundsException {
// Regression for HARMONY-387
CharArrayWriter obj = new CharArrayWriter();
try {
obj.write((String) null, -1, 0);
fail("NullPointerException expected");
} catch (NullPointerException t) {
// Expected
}
}
@Test
public void writeToLjava_io_Writer() throws IOException {
cw.write("HelloWorld", 0, 10);
StringWriter sw = new StringWriter();
cw.writeTo(sw);
assertEquals("Writer failed to write correct chars", "HelloWorld", sw.toString());
}
@Test
public void appendChar() {
char testChar = ' ';
CharArrayWriter writer = new CharArrayWriter(10);
writer.append(testChar);
writer.flush();
assertEquals(String.valueOf(testChar), writer.toString());
writer.close();
}
@Test
public void appendCharSequence() {
String testString = "My Test String";
CharArrayWriter writer = new CharArrayWriter(10);
writer.append(testString);
writer.flush();
assertEquals(testString, writer.toString());
writer.close();
}
@Test
public void test_appendCharSequenceIntInt() {
String testString = "My Test String";
CharArrayWriter writer = new CharArrayWriter(10);
writer.append(testString, 1, 3);
writer.flush();
assertEquals(testString.substring(1, 3), writer.toString());
writer.close();
}
}