Merge branch 'master' into async-irreducible

This commit is contained in:
Alexey Andreev 2015-02-27 21:29:22 +03:00
commit 59408e40e5
44 changed files with 1355 additions and 613 deletions

View File

@ -25,7 +25,7 @@ public abstract class Charset {
public abstract void decode(ByteBuffer source, CharBuffer dest); public abstract void decode(ByteBuffer source, CharBuffer dest);
public static Charset get(String name) { public static Charset get(String name) {
if (name.equals("UTF-8")) { if (name.toUpperCase().equals("UTF-8")) {
return new UTF8Charset(); return new UTF8Charset();
} }
return null; return null;

View File

@ -144,12 +144,12 @@ public class UnicodeSupport {
int j = 0; int j = 0;
int t = 0; int t = 0;
while (true) { while (true) {
if (i == a.length) { if (j == b.length) {
while (i < a.length) { while (i < a.length) {
result[t++] = a[i++]; result[t++] = a[i++];
} }
break; break;
} else if (j == b.length) { } else if (i == a.length) {
while (j < b.length) { while (j < b.length) {
result[t++] = b[j++]; result[t++] = b[j++];
} }

View File

@ -39,7 +39,7 @@ public class TBufferedInputStream extends TFilterInputStream {
} }
@Override @Override
public synchronized int available() throws TIOException { public int available() throws TIOException {
TInputStream localIn = in; TInputStream localIn = in;
if (buf == null || localIn == null) { if (buf == null || localIn == null) {
throw new TIOException(TString.wrap("Stream is closed")); throw new TIOException(TString.wrap("Stream is closed"));

View File

@ -51,7 +51,7 @@ public class TByteArrayOutputStream extends TOutputStream {
private void ensureCapacity(int capacity) { private void ensureCapacity(int capacity) {
if (buf.length < capacity) { if (buf.length < capacity) {
capacity = TMath.min(capacity, buf.length * 3 / 2); capacity = TMath.max(capacity, buf.length * 3 / 2);
buf = TArrays.copyOf(buf, capacity); buf = TArrays.copyOf(buf, capacity);
} }
} }

View File

@ -17,6 +17,7 @@
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.*; import org.teavm.classlib.java.lang.*;
/** /**
* A data output stream lets an application write primitive Java data types to an output stream in a portable way. An application can then use a data input stream to read the data back in. * A data output stream lets an application write primitive Java data types to an output stream in a portable way. An application can then use a data input stream to read the data back in.
* Since: JDK1.0, CLDC 1.0 See Also:DataInputStream * Since: JDK1.0, CLDC 1.0 See Also:DataInputStream
@ -33,7 +34,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* {@code out}. Note that data written by this stream is not in a human * {@code out}. Note that data written by this stream is not in a human
* readable form but can be reconstructed by using a {@link DataInputStream} * readable form but can be reconstructed by using a {@link DataInputStream}
* on the resulting output. * on the resulting output.
* *
* @param out * @param out
* the target stream for writing. * the target stream for writing.
*/ */
@ -45,7 +46,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Flushes this stream to ensure all pending data is sent out to the target * Flushes this stream to ensure all pending data is sent out to the target
* stream. This implementation then also flushes the target stream. * stream. This implementation then also flushes the target stream.
* *
* @throws IOException * @throws IOException
* if an error occurs attempting to flush this stream. * if an error occurs attempting to flush this stream.
*/ */
@ -56,7 +57,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Returns the total number of bytes written to the target stream so far. * Returns the total number of bytes written to the target stream so far.
* *
* @return the number of bytes written to the target stream. * @return the number of bytes written to the target stream.
*/ */
public final int size() { public final int size() {
@ -69,7 +70,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes {@code count} bytes from the byte array {@code buffer} starting at * Writes {@code count} bytes from the byte array {@code buffer} starting at
* {@code offset} to the target stream. * {@code offset} to the target stream.
* *
* @param buffer * @param buffer
* the buffer to write to the target stream. * the buffer to write to the target stream.
* @param offset * @param offset
@ -95,7 +96,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes a byte to the target stream. Only the least significant byte of * Writes a byte to the target stream. Only the least significant byte of
* the integer {@code oneByte} is written. * the integer {@code oneByte} is written.
* *
* @param oneByte * @param oneByte
* the byte to write to the target stream. * the byte to write to the target stream.
* @throws IOException * @throws IOException
@ -110,13 +111,14 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes a boolean to the target stream. * Writes a boolean to the target stream.
* *
* @param val * @param val
* the boolean value to write to the target stream. * the boolean value to write to the target stream.
* @throws IOException * @throws IOException
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readBoolean() * @see DataInputStream#readBoolean()
*/ */
@Override
public final void writeBoolean(boolean val) throws TIOException { public final void writeBoolean(boolean val) throws TIOException {
out.write(val ? 1 : 0); out.write(val ? 1 : 0);
written++; written++;
@ -125,7 +127,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes an 8-bit byte to the target stream. Only the least significant * Writes an 8-bit byte to the target stream. Only the least significant
* byte of the integer {@code val} is written. * byte of the integer {@code val} is written.
* *
* @param val * @param val
* the byte value to write to the target stream. * the byte value to write to the target stream.
* @throws IOException * @throws IOException
@ -133,6 +135,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* @see DataInputStream#readByte() * @see DataInputStream#readByte()
* @see DataInputStream#readUnsignedByte() * @see DataInputStream#readUnsignedByte()
*/ */
@Override
public final void writeByte(int val) throws TIOException { public final void writeByte(int val) throws TIOException {
out.write(val); out.write(val);
written++; written++;
@ -140,7 +143,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes the low order bytes from a string to the target stream. * Writes the low order bytes from a string to the target stream.
* *
* @param str * @param str
* the string containing the bytes to write to the target stream. * the string containing the bytes to write to the target stream.
* @throws IOException * @throws IOException
@ -164,13 +167,14 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* Writes a 16-bit character to the target stream. Only the two lower bytes * Writes a 16-bit character to the target stream. Only the two lower bytes
* of the integer {@code val} are written, with the higher one written * of the integer {@code val} are written, with the higher one written
* first. This corresponds to the Unicode value of {@code val}. * first. This corresponds to the Unicode value of {@code val}.
* *
* @param val * @param val
* the character to write to the target stream * the character to write to the target stream
* @throws IOException * @throws IOException
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readChar() * @see DataInputStream#readChar()
*/ */
@Override
public final void writeChar(int val) throws TIOException { public final void writeChar(int val) throws TIOException {
buff[0] = (byte) (val >> 8); buff[0] = (byte) (val >> 8);
buff[1] = (byte) val; buff[1] = (byte) val;
@ -181,7 +185,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes the 16-bit characters contained in {@code str} to the target * Writes the 16-bit characters contained in {@code str} to the target
* stream. * stream.
* *
* @param str * @param str
* the string that contains the characters to write to this * the string that contains the characters to write to this
* stream. * stream.
@ -189,6 +193,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readChar() * @see DataInputStream#readChar()
*/ */
@Override
public final void writeChars(TString str) throws TIOException { public final void writeChars(TString str) throws TIOException {
byte newBytes[] = new byte[str.length() * 2]; byte newBytes[] = new byte[str.length() * 2];
for (int index = 0; index < str.length(); index++) { for (int index = 0; index < str.length(); index++) {
@ -203,13 +208,14 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes a 64-bit double to the target stream. The resulting output is the * Writes a 64-bit double to the target stream. The resulting output is the
* eight bytes resulting from calling Double.doubleToLongBits(). * eight bytes resulting from calling Double.doubleToLongBits().
* *
* @param val * @param val
* the double to write to the target stream. * the double to write to the target stream.
* @throws IOException * @throws IOException
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readDouble() * @see DataInputStream#readDouble()
*/ */
@Override
public final void writeDouble(double val) throws TIOException { public final void writeDouble(double val) throws TIOException {
writeLong(TDouble.doubleToLongBits(val)); writeLong(TDouble.doubleToLongBits(val));
} }
@ -217,13 +223,14 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes a 32-bit float to the target stream. The resulting output is the * Writes a 32-bit float to the target stream. The resulting output is the
* four bytes resulting from calling Float.floatToIntBits(). * four bytes resulting from calling Float.floatToIntBits().
* *
* @param val * @param val
* the float to write to the target stream. * the float to write to the target stream.
* @throws IOException * @throws IOException
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readFloat() * @see DataInputStream#readFloat()
*/ */
@Override
public final void writeFloat(float val) throws TIOException { public final void writeFloat(float val) throws TIOException {
writeInt(TFloat.floatToIntBits(val)); writeInt(TFloat.floatToIntBits(val));
} }
@ -231,13 +238,14 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes a 32-bit int to the target stream. The resulting output is the * Writes a 32-bit int to the target stream. The resulting output is the
* four bytes, highest order first, of {@code val}. * four bytes, highest order first, of {@code val}.
* *
* @param val * @param val
* the int to write to the target stream. * the int to write to the target stream.
* @throws IOException * @throws IOException
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readInt() * @see DataInputStream#readInt()
*/ */
@Override
public final void writeInt(int val) throws TIOException { public final void writeInt(int val) throws TIOException {
buff[0] = (byte) (val >> 24); buff[0] = (byte) (val >> 24);
buff[1] = (byte) (val >> 16); buff[1] = (byte) (val >> 16);
@ -250,13 +258,14 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes a 64-bit long to the target stream. The resulting output is the * Writes a 64-bit long to the target stream. The resulting output is the
* eight bytes, highest order first, of {@code val}. * eight bytes, highest order first, of {@code val}.
* *
* @param val * @param val
* the long to write to the target stream. * the long to write to the target stream.
* @throws IOException * @throws IOException
* if an error occurs while writing to the target stream. * if an error occurs while writing to the target stream.
* @see DataInputStream#readLong() * @see DataInputStream#readLong()
*/ */
@Override
public final void writeLong(long val) throws TIOException { public final void writeLong(long val) throws TIOException {
buff[0] = (byte) (val >> 56); buff[0] = (byte) (val >> 56);
buff[1] = (byte) (val >> 48); buff[1] = (byte) (val >> 48);
@ -287,7 +296,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* Writes the specified 16-bit short to the target stream. Only the lower * Writes the specified 16-bit short to the target stream. Only the lower
* two bytes of the integer {@code val} are written, with the higher one * two bytes of the integer {@code val} are written, with the higher one
* written first. * written first.
* *
* @param val * @param val
* the short to write to the target stream. * the short to write to the target stream.
* @throws IOException * @throws IOException
@ -295,6 +304,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* @see DataInputStream#readShort() * @see DataInputStream#readShort()
* @see DataInputStream#readUnsignedShort() * @see DataInputStream#readUnsignedShort()
*/ */
@Override
public final void writeShort(int val) throws TIOException { public final void writeShort(int val) throws TIOException {
buff[0] = (byte) (val >> 8); buff[0] = (byte) (val >> 8);
buff[1] = (byte) val; buff[1] = (byte) val;
@ -312,7 +322,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
/** /**
* Writes the specified encoded in {@link DataInput modified UTF-8} to this * Writes the specified encoded in {@link DataInput modified UTF-8} to this
* stream. * stream.
* *
* @param str * @param str
* the string to write to the target stream encoded in * the string to write to the target stream encoded in
* {@link DataInput modified UTF-8}. * {@link DataInput modified UTF-8}.
@ -322,15 +332,16 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
* if the encoded string is longer than 65535 bytes. * if the encoded string is longer than 65535 bytes.
* @see DataInputStream#readUTF() * @see DataInputStream#readUTF()
*/ */
@Override
public final void writeUTF(TString str) throws TIOException { public final void writeUTF(TString str) throws TIOException {
long utfCount = countUTFBytes(str); long utfCount = countUTFBytes(str);
if (utfCount > 65535) { if (utfCount > 65535) {
throw new TIOException(TString.wrap("UTF Error")); throw new TIOException(TString.wrap("UTF Error"));
} }
byte[] buffer = new byte[(int)utfCount + 2]; byte[] buffer = new byte[(int)utfCount + 2];
int offset = 0; int offset = 0;
offset = writeShortToBuffer((int) utfCount, buffer, offset); offset = writeShortToBuffer((int) utfCount, buffer, offset);
offset = writeUTFBytesToBuffer(str, (int) utfCount, buffer, offset); offset = writeUTFBytesToBuffer(str, buffer, offset);
write(buffer, 0, offset); write(buffer, 0, offset);
} }
@ -349,8 +360,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
return utfCount; return utfCount;
} }
int writeUTFBytesToBuffer(TString str, long count, int writeUTFBytesToBuffer(TString str, byte[] buffer, int offset) throws TIOException {
byte[] buffer, int offset) throws TIOException {
int length = str.length(); int length = str.length();
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
int charValue = str.charAt(i); int charValue = str.charAt(i);

View File

@ -34,7 +34,7 @@ public class TFilterInputStream extends TInputStream {
} }
@Override @Override
public synchronized void mark(int readlimit) { public void mark(int readlimit) {
in.mark(readlimit); in.mark(readlimit);
} }

View File

@ -1,92 +1,125 @@
/* /*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * Copyright 2015 Alexey Andreev.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* This code is free software; you can redistribute it and/or modify it * Licensed under the Apache License, Version 2.0 (the "License");
* under the terms of the GNU General Public License version 2 only, as * you may not use this file except in compliance with the License.
* published by the Free Software Foundation. Codename One designates this * You may obtain a copy of the License at
* particular file as subject to the "Classpath" exception as provided *
* by Oracle in the LICENSE file that accompanied this code. * http://www.apache.org/licenses/LICENSE-2.0
* *
* This code is distributed in the hope that it will be useful, but WITHOUT * Unless required by applicable law or agreed to in writing, software
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * distributed under the License is distributed on an "AS IS" BASIS,
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* version 2 for more details (a copy is included in the LICENSE file that * See the License for the specific language governing permissions and
* accompanied this code). * limitations under the License.
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/ */
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.*;
/** import org.teavm.classlib.impl.charset.ByteBuffer;
* An OutputStreamWriter is a bridge from character streams to byte streams: Characters written to it are translated into bytes. The encoding that it uses may be specified by name, or the platform's default encoding may be accepted. import org.teavm.classlib.impl.charset.CharBuffer;
* Each invocation of a write() method causes the encoding converter to be invoked on the given character(s). The resulting bytes are accumulated in a buffer before being written to the underlying output stream. The size of this buffer may be specified, but by default it is large enough for most purposes. Note that the characters passed to the write() methods are not buffered. import org.teavm.classlib.impl.charset.Charset;
* Since: CLDC 1.0 See Also:Writer, UnsupportedEncodingException import org.teavm.classlib.java.lang.TString;
*/
public class TOutputStreamWriter extends TWriter{ public class TOutputStreamWriter extends TWriter {
private TOutputStream os; private TOutputStream out;
private TString enc; private String encoding;
private Charset charset;
/** private byte[] bufferData = new byte[512];
* Create an OutputStreamWriter that uses the default character encoding. private ByteBuffer buffer = new ByteBuffer(bufferData);
* os - An OutputStream
*/ public TOutputStreamWriter(TOutputStream out) {
public TOutputStreamWriter(TOutputStream os){ this(out, "UTF-8");
this.os = os;
enc = TString.wrap("UTF-8");
} }
/** public TOutputStreamWriter(TOutputStream out, final String enc) throws TUnsupportedEncodingException {
* Create an OutputStreamWriter that uses the named character encoding. super(out);
* os - An OutputStreamenc - The name of a supported if (enc == null) {
* - If the named encoding is not supported throw new NullPointerException();
*/
public TOutputStreamWriter(TOutputStream os, TString enc) throws TUnsupportedEncodingException{
this.os = os;
this.enc = enc;
}
/**
* Close the stream.
*/
public void close() throws TIOException{
os.close();
}
/**
* Flush the stream.
*/
public void flush() throws TIOException{
os.flush();
}
/**
* Write a portion of an array of characters.
*/
public void write(char[] cbuf, int off, int len) throws TIOException{
write(new TString(cbuf, off, len));
}
/**
* Write a single character.
*/
public void write(int c) throws TIOException{
write(new TString(new char[] {(char)c}));
}
/**
* Write a portion of a string.
*/
public void write(TString str, int off, int len) throws TIOException{
if(off > 0 || len != str.length()) {
str = str.substring(off, len);
} }
os.write(str.getBytes(enc)); this.out = out;
charset = Charset.get(enc);
if (charset == null) {
throw new TUnsupportedEncodingException(TString.wrap(enc));
}
encoding = enc;
} }
@Override
public void close() throws TIOException {
if (charset != null) {
flush();
charset = null;
out.flush();
out.close();
}
}
@Override
public void flush() throws TIOException {
checkStatus();
if (buffer.position() > 0) {
out.write(bufferData, 0, buffer.position());
buffer.rewind(0);
}
out.flush();
}
private void checkStatus() throws TIOException {
if (charset == null) {
throw new TIOException(TString.wrap("Writer already closed"));
}
}
public String getEncoding() {
return encoding;
}
@Override
public void write(char[] buf, int offset, int count) throws TIOException {
synchronized (lock) {
checkStatus();
if (buf == null) {
throw new NullPointerException();
}
if (offset < 0 || offset > buf.length - count || count < 0) {
throw new IndexOutOfBoundsException();
}
CharBuffer input = new CharBuffer(buf, offset, offset + count);
while (!input.end()) {
if (buffer.available() < 6) {
out.write(bufferData, 0, buffer.position());
buffer.rewind(0);
}
charset.encode(input, buffer);
}
}
}
@Override
public void write(int oneChar) throws TIOException {
synchronized (lock) {
checkStatus();
CharBuffer input = new CharBuffer(new char[] { (char)oneChar }, 0, 1);
while (!input.end()) {
if (buffer.available() < 6) {
out.write(bufferData, 0, buffer.position());
buffer.rewind(0);
}
charset.encode(input, buffer);
}
}
}
@Override
public void write(String str, int offset, int count) throws TIOException {
if (str == null) {
throw new NullPointerException();
}
if (count < 0) {
throw new IndexOutOfBoundsException("Negative count: " + count);
}
char[] chars = new char[count];
str.getChars(offset, offset + count, chars, 0);
write(chars);
}
} }

View File

@ -1,108 +1,82 @@
/* /*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * Copyright 2015 Alexey Andreev.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* This code is free software; you can redistribute it and/or modify it * Licensed under the Apache License, Version 2.0 (the "License");
* under the terms of the GNU General Public License version 2 only, as * you may not use this file except in compliance with the License.
* published by the Free Software Foundation. Codename One designates this * You may obtain a copy of the License at
* particular file as subject to the "Classpath" exception as provided *
* by Oracle in the LICENSE file that accompanied this code. * http://www.apache.org/licenses/LICENSE-2.0
* *
* This code is distributed in the hope that it will be useful, but WITHOUT * Unless required by applicable law or agreed to in writing, software
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * distributed under the License is distributed on an "AS IS" BASIS,
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* version 2 for more details (a copy is included in the LICENSE file that * See the License for the specific language governing permissions and
* accompanied this code). * limitations under the License.
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/ */
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.*; import org.teavm.classlib.java.lang.TAppendable;
/** import org.teavm.classlib.java.lang.TCharSequence;
* Abstract class for writing to character streams. The only methods that a subclass must implement are write(char[], int, int), flush(), and close(). Most subclasses, however, will override some of the methods defined here in order to provide higher efficiency, additional functionality, or both.
* Since: JDK1.1, CLDC 1.0 See Also:OutputStreamWriter, Reader
*/
public abstract class TWriter extends TObject{
/**
* The object used to synchronize operations on this stream. For efficiency, a character-stream object may use an object other than itself to protect critical sections. A subclass should therefore use the object in this field rather than this or a synchronized method.
*/
protected TObject lock;
/** public abstract class TWriter implements TAppendable, TCloseable, TFlushable {
* Create a new character-stream writer whose critical sections will synchronize on the writer itself. protected Object lock;
*/
protected TWriter(){ protected TWriter() {
super();
lock = this; lock = this;
} }
/** protected TWriter(Object lock) {
* Create a new character-stream writer whose critical sections will synchronize on the given object. if (lock == null) {
* lock - Object to synchronize on. throw new NullPointerException();
*/ }
protected TWriter(TObject lock){
this.lock = lock; this.lock = lock;
} }
/** public void write(char buf[]) throws TIOException {
* Close the stream, flushing it first. Once a stream has been closed, further write() or flush() invocations will cause an IOException to be thrown. Closing a previously-closed stream, however, has no effect. write(buf, 0, buf.length);
*/
public abstract void close() throws TIOException;
/**
* Flush the stream. If the stream has saved any characters from the various write() methods in a buffer, write them immediately to their intended destination. Then, if that destination is another character or byte stream, flush it. Thus one flush() invocation will flush all the buffers in a chain of Writers and OutputStreams.
*/
public abstract void flush() throws TIOException;
/**
* Write an array of characters.
*/
public void write(char[] cbuf) throws TIOException{
write(cbuf, 0, cbuf.length);
} }
/** public abstract void write(char buf[], int offset, int count) throws TIOException;
* Write a portion of an array of characters.
*/
public abstract void write(char[] cbuf, int off, int len) throws TIOException;
/** public void write(int oneChar) throws TIOException {
* Write a single character. The character to be written is contained in the 16 low-order bits of the given integer value; the 16 high-order bits are ignored.
* Subclasses that intend to support efficient single-character output should override this method.
*/
public void write(int c) throws TIOException{
synchronized (lock) { synchronized (lock) {
char oneCharArray[] = new char[1]; char oneCharArray[] = new char[1];
oneCharArray[0] = (char) c; oneCharArray[0] = (char) oneChar;
write(oneCharArray); write(oneCharArray);
} }
} }
/** public void write(String str) throws TIOException {
* Write a string.
*/
public void write(TString str) throws TIOException{
write(str, 0, str.length()); write(str, 0, str.length());
} }
/** public void write(String str, int offset, int count) throws TIOException {
* Write a portion of a string. if (count < 0) {
*/
public void write(TString str, int off, int len) throws TIOException{
if (len < 0) {
throw new StringIndexOutOfBoundsException(); throw new StringIndexOutOfBoundsException();
} }
char buf[] = new char[len]; char buf[] = new char[count];
str.getChars(off, off + len, buf, 0); str.getChars(offset, offset + count, buf, 0);
synchronized (lock) { synchronized (lock) {
write(buf, 0, buf.length); write(buf, 0, buf.length);
} }
} }
@Override
public TWriter append(char c) throws TIOException {
write(c);
return this;
}
@Override
public TWriter append(TCharSequence csq) throws TIOException {
write(csq != null ? csq.toString() : "null");
return this;
}
@Override
public TWriter append(TCharSequence csq, int start, int end) throws TIOException {
write(csq != null ? csq.subSequence(start, end).toString() : "null");
return this;
}
} }

View File

@ -249,7 +249,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
exp = 0; exp = 0;
float digit = 1; float digit = 1;
for (int i = powersOfTen.length - 1; i >= 0; --i) { for (int i = powersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= FLOAT_MAX_EXPONENT && powersOfTen[i] * digit < value) { if ((exp | bit) <= FLOAT_MAX_EXPONENT && powersOfTen[i] * digit <= value) {
digit *= powersOfTen[i]; digit *= powersOfTen[i];
exp |= bit; exp |= bit;
} }
@ -290,7 +290,6 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
exp = 0; exp = 0;
} }
} }
sz += digits;
// Extend buffer to store exponent // Extend buffer to store exponent
if (exp != 0) { if (exp != 0) {
@ -303,6 +302,11 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
} }
} }
if (exp != 0 && digits == intPart) {
digits++;
}
sz += digits;
// Print mantissa // Print mantissa
insertSpace(target, target + sz); insertSpace(target, target + sz);
if (negative) { if (negative) {
@ -399,7 +403,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
exp = 0; exp = 0;
double digit = 1; double digit = 1;
for (int i = doublePowersOfTen.length - 1; i >= 0; --i) { for (int i = doublePowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= DOUBLE_MAX_EXPONENT && doublePowersOfTen[i] * digit < value) { if ((exp | bit) <= DOUBLE_MAX_EXPONENT && doublePowersOfTen[i] * digit <= value) {
digit *= doublePowersOfTen[i]; digit *= doublePowersOfTen[i];
exp |= bit; exp |= bit;
} }
@ -440,7 +444,6 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
exp = 0; exp = 0;
} }
} }
sz += digits;
// Extend buffer to store exponent // Extend buffer to store exponent
if (exp != 0) { if (exp != 0) {
@ -456,6 +459,11 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
} }
} }
if (exp != 0 && digits == intPart) {
digits++;
}
sz += digits;
// Print mantissa // Print mantissa
insertSpace(target, target + sz); insertSpace(target, target + sz);
if (negative) { if (negative) {

View File

@ -96,16 +96,20 @@ public class TObject {
} }
o.monitor.owner = null; o.monitor.owner = null;
Platform.startThread(new PlatformRunnable() { if (!o.monitor.enteringThreads.isEmpty()) {
@Override public void run() { Platform.startThread(new PlatformRunnable() {
if (o.isEmptyMonitor() || o.monitor.owner != null) { @Override public void run() {
return; if (o.isEmptyMonitor() || o.monitor.owner != null) {
return;
}
if (!o.monitor.enteringThreads.isEmpty()) {
o.monitor.enteringThreads.remove().run();
}
} }
if (!o.monitor.enteringThreads.isEmpty()) { });
o.monitor.enteringThreads.remove().run(); } else {
} o.isEmptyMonitor();
} }
});
} }
boolean isEmptyMonitor() { boolean isEmptyMonitor() {
@ -216,6 +220,9 @@ public class TObject {
@Rename("wait") @Rename("wait")
public final void wait0(long timeout, int nanos, final AsyncCallback<Void> callback) { public final void wait0(long timeout, int nanos, final AsyncCallback<Void> callback) {
if (!holdsLock(this)) {
throw new TIllegalMonitorStateException();
}
final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count); final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count);
monitor.notifyListeners.add(listener); monitor.notifyListeners.add(listener);
if (timeout > 0 || nanos > 0) { if (timeout > 0 || nanos > 0) {

View File

@ -101,7 +101,7 @@ public class TThrowable extends RuntimeException {
} }
@Override @Override
public synchronized Throwable fillInStackTrace() { public Throwable fillInStackTrace() {
return this; return this;
} }
@ -116,7 +116,7 @@ public class TThrowable extends RuntimeException {
} }
@Override @Override
public synchronized TThrowable getCause() { public TThrowable getCause() {
return cause != this ? cause : null; return cause != this ? cause : null;
} }
@ -126,7 +126,7 @@ public class TThrowable extends RuntimeException {
@Remove @Remove
public native TString toString0(); public native TString toString0();
public synchronized TThrowable initCause(TThrowable cause) { public TThrowable initCause(TThrowable cause) {
if (this.cause != this && this.cause != null) { if (this.cause != this && this.cause != null) {
throw new TIllegalStateException(TString.wrap("Cause already set")); throw new TIllegalStateException(TString.wrap("Cause already set"));
} }

View File

@ -293,7 +293,7 @@ public abstract class TCalendar implements TSerializable, TCloneable, TComparabl
return value; return value;
} }
public static synchronized TLocale[] getAvailableLocales() { public static TLocale[] getAvailableLocales() {
return TLocale.getAvailableLocales(); return TLocale.getAvailableLocales();
} }
@ -303,11 +303,11 @@ public abstract class TCalendar implements TSerializable, TCloneable, TComparabl
abstract public int getGreatestMinimum(int field); abstract public int getGreatestMinimum(int field);
public static synchronized TCalendar getInstance() { public static TCalendar getInstance() {
return new TGregorianCalendar(); return new TGregorianCalendar();
} }
public static synchronized TCalendar getInstance(TLocale locale) { public static TCalendar getInstance(TLocale locale) {
return new TGregorianCalendar(locale); return new TGregorianCalendar(locale);
} }

View File

@ -244,11 +244,11 @@ public final class TLocale implements TCloneable, TSerializable {
} }
@Override @Override
public synchronized int hashCode() { public int hashCode() {
return countryCode.hashCode() + languageCode.hashCode() + variantCode.hashCode(); return countryCode.hashCode() + languageCode.hashCode() + variantCode.hashCode();
} }
public synchronized static void setDefault(TLocale locale) { public static void setDefault(TLocale locale) {
if (locale != null) { if (locale != null) {
defaultLocale = locale; defaultLocale = locale;
} else { } else {

View File

@ -0,0 +1,34 @@
/*
* Copyright 2015 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.impl.unicode;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class UnicodeSupportTest {
private static boolean pairsEqual(final int[] pairs, final int index1, final int index2) {
return pairs[index1] == pairs[index2] && pairs[index1 + 1] == pairs[index2 + 1];
}
@Test
public void test_getDigitValues() {
final int[] digitValues = UnicodeSupport.getDigitValues();
if (digitValues.length >= 4) {
// there are no duplicates, so the last two pairs should not be identical
assertFalse(pairsEqual(digitValues, digitValues.length - 4, digitValues.length - 2));
}
}
}

View File

@ -80,6 +80,11 @@ public class DefaultNamingStrategy implements NamingStrategy {
return getFullNameFor(method, 'S'); return getFullNameFor(method, 'S');
} }
@Override
public String getFullNameForAsync(MethodReference method) throws NamingException {
return getFullNameFor(method, 'A');
}
@Override @Override
public String getNameForInit(MethodReference method) throws NamingException { public String getNameForInit(MethodReference method) throws NamingException {
return getFullNameFor(method, 'I'); return getFullNameFor(method, 'I');

View File

@ -33,5 +33,7 @@ public interface NamingStrategy {
String getFullNameFor(MethodReference method) throws NamingException; String getFullNameFor(MethodReference method) throws NamingException;
String getFullNameForAsync(MethodReference method) throws NamingException;
String getNameFor(FieldReference field) throws NamingException; String getNameFor(FieldReference field) throws NamingException;
} }

View File

@ -496,8 +496,7 @@ class DependencyGraphBuilder {
@Override @Override
public void monitorEnter(VariableReader objectRef) { public void monitorEnter(VariableReader objectRef) {
MethodDependency methodDep = dependencyChecker.linkMethod( MethodDependency methodDep = dependencyChecker.linkMethod(
new MethodReference(Object.class, "monitorEnter", Object.class, void.class), new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null);
new CallLocation(caller.getMethod(), currentLocation));
nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); nodes[objectRef.getIndex()].connect(methodDep.getVariable(1));
methodDep.use(); methodDep.use();
} }
@ -505,8 +504,7 @@ class DependencyGraphBuilder {
@Override @Override
public void monitorExit(VariableReader objectRef) { public void monitorExit(VariableReader objectRef) {
MethodDependency methodDep = dependencyChecker.linkMethod( MethodDependency methodDep = dependencyChecker.linkMethod(
new MethodReference(Object.class, "monitorExit", Object.class, void.class), new MethodReference(Object.class, "monitorExit", Object.class, void.class), null);
new CallLocation(caller.getMethod(), currentLocation));
nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); nodes[objectRef.getIndex()].connect(methodDep.getVariable(1));
methodDep.use(); methodDep.use();
} }

View File

@ -71,34 +71,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
boolean wasGrouped; boolean wasGrouped;
} }
@Override
public void visit(MonitorEnterStatement statement) {
try {
MethodReference monitorEnterRef = new MethodReference(
Object.class, "monitorEnter", Object.class, void.class);
writer.appendMethodBody(monitorEnterRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(",").ws();
writer.append("$rt_continue($part_").append(statement.getAsyncTarget()).append(')');
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
}
@Override
public void visit(MonitorExitStatement statement) {
try {
MethodReference monitorExitRef = new MethodReference(
Object.class, "monitorExit", Object.class, void.class);
writer.appendMethodBody(monitorExitRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
}
private static class InjectorHolder { private static class InjectorHolder {
public final Injector injector; public final Injector injector;
@ -431,7 +403,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append("],").ws(); writer.append("],").ws();
int flags = 0; int flags = 0;
if (cls.getModifiers().contains(NodeModifier.ENUM)) { if (cls.getModifiers().contains(NodeModifier.ENUM)) {
flags &= 1; flags |= 1;
} }
writer.append(flags).append(',').ws(); writer.append(flags).append(',').ws();
MethodHolder clinit = classSource.get(cls.getName()).getMethod( MethodHolder clinit = classSource.get(cls.getName()).getMethod(
@ -448,7 +420,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
for (MethodNode method : cls.getMethods()) { for (MethodNode method : cls.getMethods()) {
if (clinit != null && (method.getModifiers().contains(NodeModifier.STATIC) || if (clinit != null && (method.getModifiers().contains(NodeModifier.STATIC) ||
method.getReference().getName().equals("<init>"))) { method.getReference().getName().equals("<init>"))) {
stubNames.add(naming.getFullNameFor(method.getReference())); if (!method.isAsync()) {
stubNames.add(naming.getFullNameFor(method.getReference()));
}
if (asyncFamilyMethods.contains(method.getReference())) {
stubNames.add(naming.getFullNameForAsync(method.getReference()));
}
} }
if (!method.getModifiers().contains(NodeModifier.STATIC)) { if (!method.getModifiers().contains(NodeModifier.STATIC)) {
virtualMethods.add(method); virtualMethods.add(method);
@ -537,51 +514,58 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(",").ws(); writer.append(",").ws();
} }
first = false; first = false;
String methodName = method.isAsync() ? naming.getNameForAsync(ref) : naming.getNameFor(ref);
writer.append("\"").append(methodName).append("\"");
writer.append(",").ws().append("function(");
List<String> args = new ArrayList<>();
for (int i = 1; i <= ref.parameterCount(); ++i) {
args.add(variableName(i));
}
if (method.isAsync()) { if (method.isAsync()) {
args.add("$return"); emitVirtualDeclaration(ref, true);
} } else {
for (int i = 0; i < args.size(); ++i) { emitVirtualDeclaration(ref, false);
if (i > 0) { if (asyncFamilyMethods.contains(ref)) {
writer.append(",").ws(); writer.append(",").ws();
emitVirtualDeclaration(ref, true);
} }
writer.append(args.get(i));
} }
writer.append(")").ws().append("{").ws();
if (ref.getDescriptor().getResultType() != ValueType.VOID) {
writer.append("return ");
}
writer.appendMethodBody(ref).append("(");
writer.append("this");
for (int i = 0; i < args.size(); ++i) {
writer.append(",").ws().append(args.get(i));
}
writer.append(");").ws().append("}");
debugEmitter.emitMethod(null); debugEmitter.emitMethod(null);
if (!method.isAsync() && asyncFamilyMethods.contains(method.getReference())) {
writer.append(",").newLine();
writer.append("\"").append(naming.getNameForAsync(ref)).append("\",").ws();
writer.append("$rt_asyncAdapter(").appendMethodBody(ref).append(')');
}
} }
writer.append("]"); writer.append("]");
} }
private void emitVirtualDeclaration(MethodReference ref, boolean async) throws IOException {
String methodName = async ? naming.getNameForAsync(ref) : naming.getNameFor(ref);
writer.append("\"").append(methodName).append("\"");
writer.append(",").ws().append("function(");
List<String> args = new ArrayList<>();
for (int i = 1; i <= ref.parameterCount(); ++i) {
args.add(variableName(i));
}
if (async) {
args.add("$return");
}
for (int i = 0; i < args.size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
writer.append(args.get(i));
}
writer.append(")").ws().append("{").ws();
if (ref.getDescriptor().getResultType() != ValueType.VOID) {
writer.append("return ");
}
writer.append(async ? naming.getFullNameForAsync(ref) : naming.getFullNameFor(ref)).append("(");
writer.append("this");
for (int i = 0; i < args.size(); ++i) {
writer.append(",").ws().append(args.get(i));
}
writer.append(");").ws().append("}");
}
public void renderBody(MethodNode method, boolean inner) throws IOException { public void renderBody(MethodNode method, boolean inner) throws IOException {
blockIdMap.clear(); blockIdMap.clear();
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref.getDescriptor()); debugEmitter.emitMethod(ref.getDescriptor());
String name = method.isAsync() ? naming.getFullNameForAsync(ref) : naming.getFullNameFor(ref);
if (inner) { if (inner) {
writer.appendMethodBody(ref).ws().append("=").ws().append("function("); writer.append(name).ws().append("=").ws().append("function(");
} else { } else {
writer.append("function ").appendMethodBody(ref).append("("); writer.append("function ").append(name).append("(");
} }
int startParam = 0; int startParam = 0;
if (method.getModifiers().contains(NodeModifier.STATIC)) { if (method.getModifiers().contains(NodeModifier.STATIC)) {
@ -606,6 +590,40 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(';'); writer.append(';');
} }
writer.newLine(); writer.newLine();
if (!method.isAsync() && asyncFamilyMethods.contains(method.getReference())) {
if (inner) {
writer.append(naming.getFullNameForAsync(ref)).ws().append("=").ws().append("function(");
} else {
writer.append("function ").append(naming.getFullNameForAsync(ref)).append("(");
}
for (int i = startParam; i <= ref.parameterCount(); ++i) {
writer.append(variableName(i));
writer.append(",").ws();
}
writer.append("$return)").ws().append("{").softNewLine().indent();
writer.append("var $r;").softNewLine();
writer.append("try").ws().append('{').indent().softNewLine();
writer.append("$r").ws().append("=").ws().appendMethodBody(ref).append('(');
for (int i = startParam; i <= ref.parameterCount(); ++i) {
if (i > startParam) {
writer.append(",").ws();
}
writer.append(variableName(i));
}
writer.append(");").softNewLine();
writer.outdent().append("}").ws().append("catch").ws().append("($e)").ws()
.append("{").indent().softNewLine();
writer.append("return $return($rt_asyncError($e));").softNewLine();
writer.outdent().append("}");
writer.append("$return($rt_asyncResult($r));").softNewLine();
writer.outdent().append("}");
if (inner) {
writer.append(';');
}
writer.newLine();
}
debugEmitter.emitMethod(null); debugEmitter.emitMethod(null);
} }
@ -752,6 +770,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
return asyncMethods.contains(method); return asyncMethods.contains(method);
} }
@Override
public boolean isAsyncFamily(MethodReference method) {
return asyncFamilyMethods.contains(method);
}
@Override @Override
public String getCompleteContinuation() { public String getCompleteContinuation() {
return "$return"; return "$return";
@ -1071,6 +1094,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override @Override
public void visit(InitClassStatement statement) { public void visit(InitClassStatement statement) {
ClassReader cls = classSource.get(statement.getClassName());
if (cls == null) {
return;
}
MethodReader method = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
if (method == null) {
return;
}
try { try {
debugEmitter.emitStatementStart(); debugEmitter.emitStatementStart();
if (statement.getLocation() != null) { if (statement.getLocation() != null) {
@ -1287,7 +1318,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
exitPriority(); exitPriority();
break; break;
case NEGATE: case NEGATE:
enterPriority(Priority.UNARY, Associativity.RIGHT, true); enterPriority(Priority.MULTIPLICATION, Associativity.RIGHT, true);
writer.append("-"); writer.append("-");
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
exitPriority(); exitPriority();
@ -1339,20 +1370,26 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(')'); writer.append(')');
exitPriority(); exitPriority();
break; break;
case BYTE_TO_INT: case INT_TO_BYTE:
enterPriority(Priority.BITWISE_SHIFT, Associativity.LEFT, true); enterPriority(Priority.BITWISE_SHIFT, Associativity.LEFT, true);
writer.append("("); writer.append("(");
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
writer.ws().append("<<").ws().append("24)").ws().append(">>").ws().append("24"); writer.ws().append("<<").ws().append("24)").ws().append(">>").ws().append("24");
exitPriority(); exitPriority();
break; break;
case SHORT_TO_INT: case INT_TO_SHORT:
enterPriority(Priority.BITWISE_SHIFT, Associativity.LEFT, true); enterPriority(Priority.BITWISE_SHIFT, Associativity.LEFT, true);
writer.append("("); writer.append("(");
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
writer.ws().append("<<").ws().append("16)").ws().append(">>").ws().append("16"); writer.ws().append("<<").ws().append("16)").ws().append(">>").ws().append("16");
exitPriority(); exitPriority();
break; break;
case INT_TO_CHAR:
enterPriority(Priority.BITWISE_AND, Associativity.LEFT, true);
expr.getOperand().acceptVisitor(this);
writer.ws().append("&").ws().append("65535");
exitPriority();
break;
case NULL_CHECK: case NULL_CHECK:
enterPriority(Priority.COMMA, Associativity.NONE, false); enterPriority(Priority.COMMA, Associativity.NONE, false);
writer.append("$rt_nullCheck("); writer.append("$rt_nullCheck(");
@ -1396,7 +1433,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (expr.getLocation() != null) { if (expr.getLocation() != null) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
} }
writer.append(constantToString(expr.getValue())); String str = constantToString(expr.getValue());
if (str.startsWith("-")) {
enterPriority(Priority.MULTIPLICATION, Associativity.RIGHT, true);
}
writer.append(str);
if (str.startsWith("-")) {
exitPriority();
}
if (expr.getLocation() != null) { if (expr.getLocation() != null) {
popLocation(); popLocation();
} }
@ -1596,14 +1640,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (injector != null) { if (injector != null) {
injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod()); injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod());
} else { } else {
if (expr.getAsyncTarget() != null) { boolean asyncCall = expr.getAsyncTarget() != null;
if (asyncCall) {
writer.append("return "); writer.append("return ");
} }
if (expr.getType() == InvocationType.DYNAMIC) { if (expr.getType() == InvocationType.DYNAMIC) {
expr.getArguments().get(0).acceptVisitor(this); expr.getArguments().get(0).acceptVisitor(this);
} }
String name = expr.getAsyncTarget() == null ? naming.getNameFor(expr.getMethod()) : MethodReference method = expr.getMethod();
naming.getNameForAsync(expr.getMethod()); String name = asyncCall ? naming.getNameForAsync(method) : naming.getNameFor(method);
DeferredCallSite callSite = prevCallSite; DeferredCallSite callSite = prevCallSite;
boolean shouldEraseCallSite = lastCallSite == null; boolean shouldEraseCallSite = lastCallSite == null;
if (lastCallSite == null) { if (lastCallSite == null) {
@ -1614,7 +1659,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
enterPriority(Priority.COMMA, Associativity.NONE, false); enterPriority(Priority.COMMA, Associativity.NONE, false);
switch (expr.getType()) { switch (expr.getType()) {
case STATIC: case STATIC:
writer.appendMethodBody(expr.getMethod()).append("("); writer.append(asyncCall ? naming.getFullNameForAsync(method) :
naming.getFullNameFor(method)).append("(");
prevCallSite = debugEmitter.emitCallSite(); prevCallSite = debugEmitter.emitCallSite();
for (int i = 0; i < expr.getArguments().size(); ++i) { for (int i = 0; i < expr.getArguments().size(); ++i) {
if (i > 0) { if (i > 0) {
@ -1625,7 +1671,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
break; break;
case SPECIAL: case SPECIAL:
writer.appendMethodBody(expr.getMethod()).append("("); writer.append(asyncCall ? naming.getFullNameForAsync(method) :
naming.getFullNameFor(method)).append("(");
prevCallSite = debugEmitter.emitCallSite(); prevCallSite = debugEmitter.emitCallSite();
expr.getArguments().get(0).acceptVisitor(this); expr.getArguments().get(0).acceptVisitor(this);
hasParams = true; hasParams = true;
@ -1952,16 +1999,54 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
} }
@Override
public void visit(MonitorEnterStatement statement) {
if (!async) {
return;
}
try {
MethodReference monitorEnterRef = new MethodReference(
Object.class, "monitorEnter", Object.class, void.class);
writer.append("return ").append(naming.getFullNameForAsync(monitorEnterRef)).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(",").ws();
writer.append("$rt_continue($part_").append(statement.getAsyncTarget()).append(')');
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
}
@Override
public void visit(MonitorExitStatement statement) {
if (!async) {
return;
}
try {
MethodReference monitorExitRef = new MethodReference(
Object.class, "monitorExit", Object.class, void.class);
writer.appendMethodBody(monitorExitRef).append("(");
statement.getObjectRef().acceptVisitor(this);
writer.append(");").softNewLine();
} catch (IOException ex){
throw new RenderingException("IO error occured", ex);
}
}
private Injector getInjector(MethodReference ref) { private Injector getInjector(MethodReference ref) {
InjectorHolder holder = injectorMap.get(ref); InjectorHolder holder = injectorMap.get(ref);
if (holder == null) { if (holder == null) {
MethodHolder method = classSource.get(ref.getClassName()).getMethod(ref.getDescriptor());
holder = new InjectorHolder(null); holder = new InjectorHolder(null);
if (method != null) { ClassHolder cls = classSource.get(ref.getClassName());
AnnotationHolder injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName()); if (cls != null) {
if (injectedByAnnot != null) { MethodHolder method = cls.getMethod(ref.getDescriptor());
ValueType type = injectedByAnnot.getValues().get("value").getJavaClass(); if (method != null) {
holder = new InjectorHolder(instantiateInjector(((ValueType.Object)type).getClassName())); AnnotationHolder injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName());
if (injectedByAnnot != null) {
ValueType type = injectedByAnnot.getValues().get("value").getJavaClass();
holder = new InjectorHolder(instantiateInjector(((ValueType.Object)type).getClassName()));
}
} }
} }
injectorMap.put(ref, holder); injectorMap.put(ref, holder);

View File

@ -304,25 +304,17 @@ class StatementGenerator implements InstructionVisitor {
case FROM_INTEGER: case FROM_INTEGER:
switch (insn.getTargetType()) { switch (insn.getTargetType()) {
case BYTE: case BYTE:
value = Expr.binary(BinaryOperation.BITWISE_AND, value, Expr.constant(0xFF)); value = Expr.unary(UnaryOperation.INT_TO_BYTE, value);
break; break;
case SHORT: case SHORT:
value = Expr.unary(UnaryOperation.INT_TO_SHORT, value);
break;
case CHARACTER: case CHARACTER:
value = Expr.binary(BinaryOperation.BITWISE_AND, value, Expr.constant(0xFFFF)); value = Expr.unary(UnaryOperation.INT_TO_CHAR, value);
break; break;
} }
break; break;
case TO_INTEGER: case TO_INTEGER:
switch (insn.getTargetType()) {
case BYTE:
value = Expr.unary(UnaryOperation.BYTE_TO_INT, value);
break;
case SHORT:
value = Expr.unary(UnaryOperation.SHORT_TO_INT, value);
break;
case CHARACTER:
break;
}
break; break;
} }
assign(value, insn.getReceiver()); assign(value, insn.getReceiver());

View File

@ -29,7 +29,8 @@ public enum UnaryOperation {
LONG_TO_INT, LONG_TO_INT,
NUM_TO_LONG, NUM_TO_LONG,
INT_TO_LONG, INT_TO_LONG,
BYTE_TO_INT, INT_TO_BYTE,
SHORT_TO_INT, INT_TO_SHORT,
INT_TO_CHAR,
NULL_CHECK NULL_CHECK
} }

View File

@ -40,5 +40,7 @@ public interface GeneratorContext extends ServiceRepository {
boolean isAsync(MethodReference method); boolean isAsync(MethodReference method);
boolean isAsyncFamily(MethodReference method);
Diagnostics getDiagnostics(); Diagnostics getDiagnostics();
} }

View File

@ -24,7 +24,6 @@ import org.teavm.javascript.spi.Async;
import org.teavm.javascript.spi.InjectedBy; import org.teavm.javascript.spi.InjectedBy;
import org.teavm.javascript.spi.Sync; import org.teavm.javascript.spi.Sync;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.instructions.*;
/** /**
* *
@ -62,17 +61,6 @@ public class AsyncMethodFinder {
} }
if (method.getAnnotations().get(Async.class.getName()) != null) { if (method.getAnnotations().get(Async.class.getName()) != null) {
add(method.getReference()); add(method.getReference());
} else if (method.getProgram() != null) {
ProgramReader program = method.getProgram();
AsyncInstructionFinder insnFinder = new AsyncInstructionFinder();
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i);
block.readAllInstructions(insnFinder);
if (insnFinder.hasAsync) {
add(method.getReference());
break;
}
}
} }
} }
} }
@ -197,166 +185,4 @@ public class AsyncMethodFinder {
} }
} }
} }
private class AsyncInstructionFinder implements InstructionReader {
boolean hasAsync;
@Override
public void location(InstructionLocation location) {
}
@Override
public void nop() {
}
@Override
public void classConstant(VariableReader receiver, ValueType cst) {
}
@Override
public void nullConstant(VariableReader receiver) {
}
@Override
public void integerConstant(VariableReader receiver, int cst) {
}
@Override
public void longConstant(VariableReader receiver, long cst) {
}
@Override
public void floatConstant(VariableReader receiver, float cst) {
}
@Override
public void doubleConstant(VariableReader receiver, double cst) {
}
@Override
public void stringConstant(VariableReader receiver, String cst) {
}
@Override
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) {
}
@Override
public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
}
@Override
public void assign(VariableReader receiver, VariableReader assignee) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
NumericOperandType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
CastIntegerDirection targetType) {
}
@Override
public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
BasicBlockReader alternative) {
}
@Override
public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
BasicBlockReader consequent, BasicBlockReader alternative) {
}
@Override
public void jump(BasicBlockReader target) {
}
@Override
public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
BasicBlockReader defaultTarget) {
}
@Override
public void exit(VariableReader valueToReturn) {
}
@Override
public void raise(VariableReader exception) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType,
List<? extends VariableReader> dimensions) {
}
@Override
public void create(VariableReader receiver, String type) {
}
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
ValueType fieldType) {
}
@Override
public void putField(VariableReader instance, FieldReference field, VariableReader value) {
}
@Override
public void arrayLength(VariableReader receiver, VariableReader array) {
}
@Override
public void cloneArray(VariableReader receiver, VariableReader array) {
}
@Override
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
}
@Override
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
}
@Override
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
}
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
}
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
}
@Override
public void initClass(String className) {
}
@Override
public void nullCheck(VariableReader receiver, VariableReader value) {
}
@Override
public void monitorEnter(VariableReader objectRef) {
hasAsync = true;
}
@Override
public void monitorExit(VariableReader objectRef) {
}
}
} }

View File

@ -436,13 +436,13 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
renderer.renderStringPool(); renderer.renderStringPool();
for (Map.Entry<String, TeaVMEntryPoint> entry : entryPoints.entrySet()) { for (Map.Entry<String, TeaVMEntryPoint> entry : entryPoints.entrySet()) {
sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws(); sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws();
boolean wrapAsync = !asyncMethods.contains(entry.getValue().reference) && entry.getValue().isAsync(); MethodReference ref = entry.getValue().reference;
boolean asyncMethod = asyncMethods.contains(ref);
boolean wrapAsync = !asyncMethod && entry.getValue().isAsync();
if (wrapAsync) { if (wrapAsync) {
sourceWriter.append("$rt_staticAsyncAdapter("); sourceWriter.append("$rt_staticAsyncAdapter(").appendMethodBody(ref).append(')');
} } else {
sourceWriter.appendMethodBody(entry.getValue().reference); sourceWriter.append(asyncMethod ? naming.getFullNameForAsync(ref) : naming.getFullNameFor(ref));
if (wrapAsync) {
sourceWriter.append(")");
} }
sourceWriter.append(";").newLine(); sourceWriter.append(";").newLine();
} }

View File

@ -328,23 +328,6 @@ function $rt_assertNotNaN(value) {
} }
return value; return value;
} }
function $rt_methodStubs(data) {
for (var i = 0; i < data.length; i += 2) {
var clinit = data[i + 0];
var names = data[i + 1];
if (!(names instanceof Array)) {
names = [names];
}
for (var j = 0; j < names.length; j = (j + 1) | 0) {
window[names[j]] = (function(name, clinit) {
return function() {
clinit();
return window[name].apply(window, arguments);
}
})(names[j], clinit);
}
}
}
var $rt_stdoutBuffer = ""; var $rt_stdoutBuffer = "";
function $rt_putStdout(ch) { function $rt_putStdout(ch) {
if (ch == 0xA) { if (ch == 0xA) {
@ -420,35 +403,6 @@ function $rt_metadata(data) {
} }
} }
} }
function $rt_virtualMethods(cls) {
for (var i = 1; i < arguments.length; i += 2) {
var name = arguments[i];
var func = arguments[i + 1];
if (typeof name === 'string') {
cls.prototype[name] = func;
} else {
for (var j = 0; j < name.length; ++j) {
cls.prototype[name[j]] = func;
}
}
}
}
function $rt_virtualMethods(data) {
for (var i = 0; i < data.length; i += 2) {
var cls = data[i + 0];
var methods = data[i + 1];
for (var j = 0; j < methods.length; j += 2) {
var name = methods[j + 0];
var func = methods[j + 1];
if (typeof name === 'string') {
name = [name];
}
for (var k = 0; k < name.length; ++k) {
cls.prototype[name[k]] = func;
}
}
}
}
function $rt_asyncResult(value) { function $rt_asyncResult(value) {
return function() { return function() {
return value; return value;

View File

@ -391,14 +391,19 @@ JUnitClient.run = function() {
} }
JUnitClient.makeErrorMessage = function(message, e) { JUnitClient.makeErrorMessage = function(message, e) {
message.status = "exception"; message.status = "exception";
var stack = "";
while (e instanceof TeaVMAsyncError) {
stack += e.message + "\n" + e.stack + "\n";
e = e.cause;
}
if (e.$javaException && e.$javaException.constructor.$meta) { if (e.$javaException && e.$javaException.constructor.$meta) {
message.exception = e.$javaException.constructor.$meta.name; message.exception = e.$javaException.constructor.$meta.name;
message.stack = e.$javaException.constructor.$meta.name + ": "; message.stack = e.$javaException.constructor.$meta.name + ": ";
var exceptionMessage = extractException(e.$javaException); var exceptionMessage = extractException(e.$javaException);
message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : "";
message.stack += "\n" + e.stack; message.stack += e.stack + "\n" + stack;
} else { } else {
message.stack = e.stack; message.stack = stack;
} }
} }
JUnitClient.reportError = function(error) { JUnitClient.reportError = function(error) {

View File

@ -28,12 +28,17 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
@Override @Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
processor = new JavascriptNativeProcessor(innerSource); if (processor == null || processor.getClassSource() != innerSource) {
processor = new JavascriptNativeProcessor(innerSource);
}
processor.setDiagnostics(diagnostics); processor.setDiagnostics(diagnostics);
processor.processClass(cls); processor.processClass(cls);
if (processor.isNative(cls.getName())) { if (processor.isNative(cls.getName())) {
processor.processFinalMethods(cls); processor.processFinalMethods(cls);
} }
if (processor.isNativeImplementation(cls.getName())) {
processor.makeSync(cls);
}
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
if (method.getAnnotations().get(JSBody.class.getName()) != null) { if (method.getAnnotations().get(JSBody.class.getName()) != null) {
processor.processJSBody(cls, method); processor.processJSBody(cls, method);

View File

@ -18,6 +18,7 @@ package org.teavm.jso.plugin;
import java.util.*; import java.util.*;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.javascript.spi.Sync;
import org.teavm.jso.*; import org.teavm.jso.*;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.instructions.*; import org.teavm.model.instructions.*;
@ -41,10 +42,18 @@ class JavascriptNativeProcessor {
nativeRepos = new NativeJavascriptClassRepository(classSource); nativeRepos = new NativeJavascriptClassRepository(classSource);
} }
public ClassReaderSource getClassSource() {
return classSource;
}
public boolean isNative(String className) { public boolean isNative(String className) {
return nativeRepos.isJavaScriptClass(className); return nativeRepos.isJavaScriptClass(className);
} }
public boolean isNativeImplementation(String className) {
return nativeRepos.isJavaScriptImplementation(className);
}
public void setDiagnostics(Diagnostics diagnostics) { public void setDiagnostics(Diagnostics diagnostics) {
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
} }
@ -111,6 +120,44 @@ class JavascriptNativeProcessor {
} }
} }
public void makeSync(ClassHolder cls) {
Set<MethodDescriptor> methods = new HashSet<>();
findInheritedMethods(cls, methods, new HashSet<String>());
for (MethodHolder method : cls.getMethods()) {
if (methods.contains(method.getDescriptor()) && method.getAnnotations().get(Sync.class.getName()) == null) {
AnnotationHolder annot = new AnnotationHolder(Sync.class.getName());
method.getAnnotations().add(annot);
}
}
}
private void findInheritedMethods(ClassReader cls, Set<MethodDescriptor> methods, Set<String> visited) {
if (!visited.add(cls.getName())) {
return;
}
if (isNative(cls.getName())) {
for (MethodReader method : cls.getMethods()) {
if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.FINAL) &&
method.getLevel() != AccessLevel.PRIVATE) {
methods.add(method.getDescriptor());
}
}
} else if (isNativeImplementation(cls.getName())) {
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
ClassReader parentCls = classSource.get(cls.getParent());
if (parentCls != null) {
findInheritedMethods(parentCls, methods, visited);
}
}
for (String iface : cls.getInterfaces()) {
ClassReader parentCls = classSource.get(iface);
if (parentCls != null) {
findInheritedMethods(parentCls, methods, visited);
}
}
}
}
private static ValueType[] getStaticSignature(MethodReference method) { private static ValueType[] getStaticSignature(MethodReference method) {
ValueType[] signature = method.getSignature(); ValueType[] signature = method.getSignature();
ValueType[] staticSignature = new ValueType[signature.length + 1]; ValueType[] staticSignature = new ValueType[signature.length + 1];
@ -137,7 +184,7 @@ class JavascriptNativeProcessor {
} }
replacement.clear(); replacement.clear();
MethodReader method = getMethod(invoke.getMethod()); MethodReader method = getMethod(invoke.getMethod());
if (method.hasModifier(ElementModifier.STATIC)) { if (method == null || method.hasModifier(ElementModifier.STATIC)) {
continue; continue;
} }
if (method.hasModifier(ElementModifier.FINAL)) { if (method.hasModifier(ElementModifier.FINAL)) {

View File

@ -29,6 +29,7 @@ import org.teavm.model.ElementModifier;
class NativeJavascriptClassRepository { class NativeJavascriptClassRepository {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private Map<String, Boolean> knownJavaScriptClasses = new HashMap<>(); private Map<String, Boolean> knownJavaScriptClasses = new HashMap<>();
private Map<String, Boolean> knownJavaScriptImplementations = new HashMap<>();
public NativeJavascriptClassRepository(ClassReaderSource classSource) { public NativeJavascriptClassRepository(ClassReaderSource classSource) {
this.classSource = classSource; this.classSource = classSource;
@ -38,13 +39,22 @@ class NativeJavascriptClassRepository {
public boolean isJavaScriptClass(String className) { public boolean isJavaScriptClass(String className) {
Boolean known = knownJavaScriptClasses.get(className); Boolean known = knownJavaScriptClasses.get(className);
if (known == null) { if (known == null) {
known = figureOutIfJavaScriptClass(className); known = examineIfJavaScriptClass(className);
knownJavaScriptClasses.put(className, known); knownJavaScriptClasses.put(className, known);
} }
return known; return known;
} }
private boolean figureOutIfJavaScriptClass(String className) { public boolean isJavaScriptImplementation(String className) {
Boolean known = knownJavaScriptImplementations.get(className);
if (known == null) {
known = examineIfJavaScriptImplementation(className);
knownJavaScriptImplementations.put(className, known);
}
return known;
}
private boolean examineIfJavaScriptClass(String className) {
ClassReader cls = classSource.get(className); ClassReader cls = classSource.get(className);
if (cls == null || !(cls.hasModifier(ElementModifier.INTERFACE) || cls.hasModifier(ElementModifier.ABSTRACT))) { if (cls == null || !(cls.hasModifier(ElementModifier.INTERFACE) || cls.hasModifier(ElementModifier.ABSTRACT))) {
return false; return false;
@ -56,4 +66,25 @@ class NativeJavascriptClassRepository {
} }
return false; return false;
} }
private boolean examineIfJavaScriptImplementation(String className) {
if (isJavaScriptClass(className)) {
return false;
}
ClassReader cls = classSource.get(className);
if (cls == null) {
return false;
}
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
if (isJavaScriptClass(cls.getParent())) {
return true;
}
}
for (String iface : cls.getInterfaces()) {
if (isJavaScriptClass(iface)) {
return true;
}
}
return false;
}
} }

View File

@ -20,6 +20,7 @@ import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.javascript.spi.InjectedBy; import org.teavm.javascript.spi.InjectedBy;
import org.teavm.jso.JS; import org.teavm.jso.JS;
import org.teavm.platform.metadata.ClassResource; import org.teavm.platform.metadata.ClassResource;
import org.teavm.platform.metadata.StaticFieldResource;
import org.teavm.platform.plugin.PlatformGenerator; import org.teavm.platform.plugin.PlatformGenerator;
/** /**
@ -90,6 +91,10 @@ public final class Platform {
@InjectedBy(PlatformGenerator.class) @InjectedBy(PlatformGenerator.class)
@PluggableDependency(PlatformGenerator.class) @PluggableDependency(PlatformGenerator.class)
public static native Object objectFromResource(StaticFieldResource resource);
@GeneratedBy(PlatformGenerator.class)
@PluggableDependency(PlatformGenerator.class)
public static native Enum<?>[] getEnumConstants(PlatformClass cls); public static native Enum<?>[] getEnumConstants(PlatformClass cls);
@GeneratedBy(PlatformGenerator.class) @GeneratedBy(PlatformGenerator.class)

View File

@ -17,6 +17,7 @@ package org.teavm.platform.metadata;
import java.util.Properties; import java.util.Properties;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
import org.teavm.vm.TeaVM; import org.teavm.vm.TeaVM;
@ -55,6 +56,12 @@ public interface MetadataGeneratorContext extends ServiceRepository {
*/ */
ClassResource createClassResource(String className); ClassResource createClassResource(String className);
/**
* Creates a new resource that represents static field. Client code then may use
* {@link Platform#objectFromResource(StaticFieldResource)} to get actual field value.
*/
StaticFieldResource createFieldResource(FieldReference field);
/** /**
* Creates a new resource array. * Creates a new resource array.
*/ */

View File

@ -0,0 +1,23 @@
/*
* Copyright 2015 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.platform.metadata;
/**
*
* @author Alexey Andreev
*/
public interface StaticFieldResource extends Resource {
}

View File

@ -39,12 +39,12 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin {
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
MethodReference asyncRef = getAsyncReference(methodRef); MethodReference asyncRef = getAsyncReference(methodRef);
writer.append("var callback").ws().append("=").ws().append("function()").ws().append("{};").softNewLine(); writer.append("var callback").ws().append("=").ws().append("function()").ws().append("{};").softNewLine();
writer.append("callback.").appendMethod(completeMethod).ws().append("=").ws().append("function($this,").ws() writer.append("callback.").appendMethod(completeMethod).ws().append("=").ws().append("function(val)").ws()
.append("val)").ws().append("{").indent().softNewLine(); .append("{").indent().softNewLine();
writer.append("return $return($rt_asyncResult(val));").softNewLine(); writer.append("return $return($rt_asyncResult(val));").softNewLine();
writer.outdent().append("};").softNewLine(); writer.outdent().append("};").softNewLine();
writer.append("callback.").appendMethod(errorMethod).ws().append("=").ws().append("function($this,").ws() writer.append("callback.").appendMethod(errorMethod).ws().append("=").ws().append("function(e)").ws()
.append("e)").ws().append("{").indent().softNewLine(); .append("{").indent().softNewLine();
writer.append("return $return($rt_asyncError(e));").softNewLine(); writer.append("return $return($rt_asyncError(e));").softNewLine();
writer.outdent().append("};").softNewLine(); writer.outdent().append("};").softNewLine();
writer.append("try").ws().append("{").indent().softNewLine(); writer.append("try").ws().append("{").indent().softNewLine();

View File

@ -0,0 +1,42 @@
/*
* Copyright 2015 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.platform.plugin;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
import org.teavm.model.FieldReference;
import org.teavm.platform.metadata.StaticFieldResource;
/**
*
* @author Alexey Andreev
*/
class BuildTimeStaticFieldResource implements StaticFieldResource, ResourceWriter {
private FieldReference field;
public BuildTimeStaticFieldResource(FieldReference field) {
this.field = field;
}
public FieldReference getField() {
return field;
}
@Override
public void write(SourceWriter writer) throws IOException {
writer.appendField(field);
}
}

View File

@ -18,6 +18,7 @@ package org.teavm.platform.plugin;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Properties; import java.util.Properties;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.platform.metadata.*; import org.teavm.platform.metadata.*;
@ -72,6 +73,11 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
return new BuildTimeClassResource(className); return new BuildTimeClassResource(className);
} }
@Override
public StaticFieldResource createFieldResource(FieldReference field) {
return new BuildTimeStaticFieldResource(field);
}
@Override @Override
public <T extends Resource> ResourceMap<T> createResourceMap() { public <T extends Resource> ResourceMap<T> createResourceMap() {
return new BuildTimeResourceMap<>(); return new BuildTimeResourceMap<>();

View File

@ -16,11 +16,7 @@
package org.teavm.platform.plugin; package org.teavm.platform.plugin;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.model.CallLocation; import org.teavm.model.*;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.ValueType;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
/** /**
@ -29,7 +25,6 @@ import org.teavm.platform.Platform;
*/ */
public class EnumDependencySupport implements DependencyListener { public class EnumDependencySupport implements DependencyListener {
private DependencyNode allEnums; private DependencyNode allEnums;
private boolean unlocked;
@Override @Override
public void started(DependencyAgent agent) { public void started(DependencyAgent agent) {
@ -43,21 +38,25 @@ public class EnumDependencySupport implements DependencyListener {
return; return;
} }
allEnums.propagate(agent.getType(className)); allEnums.propagate(agent.getType(className));
if (unlocked) {
MethodReader method = cls.getMethod(new MethodDescriptor("values",
ValueType.arrayOf(ValueType.object(cls.getName()))));
if (method != null) {
agent.linkMethod(method.getReference(), location).use();
}
}
} }
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { public void methodAchieved(final DependencyAgent agent, MethodDependency method, CallLocation location) {
if (method.getReference().getClassName().equals(Platform.class.getName()) && if (method.getReference().getClassName().equals(Platform.class.getName()) &&
method.getReference().getName().equals("getEnumConstants")) { method.getReference().getName().equals("getEnumConstants")) {
unlocked = true;
allEnums.connect(method.getResult().getArrayItem()); allEnums.connect(method.getResult().getArrayItem());
final MethodReference ref = method.getReference();
allEnums.addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) {
ClassReader cls = agent.getClassSource().get(type.getName());
MethodReader method = cls.getMethod(new MethodDescriptor("values",
ValueType.arrayOf(ValueType.object(cls.getName()))));
if (method != null) {
agent.linkMethod(method.getReference(), new CallLocation(ref)).use();
}
}
});
method.getResult().propagate(agent.getType("[java.lang.Enum")); method.getResult().propagate(agent.getType("[java.lang.Enum"));
for (String cls : agent.getAchievableClasses()) { for (String cls : agent.getAchievableClasses()) {
classAchieved(agent, cls, location); classAchieved(agent, cls, location);

View File

@ -47,13 +47,14 @@ public class NewInstanceDependencySupport implements DependencyListener {
} }
@Override @Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) { public void methodAchieved(final DependencyAgent agent, MethodDependency method, CallLocation location) {
MethodReader reader = method.getMethod(); MethodReader reader = method.getMethod();
if (reader.getOwnerName().equals(Platform.class.getName()) && reader.getName().equals("newInstance")) { if (reader.getOwnerName().equals(Platform.class.getName()) && reader.getName().equals("newInstance")) {
allClassesNode.connect(method.getResult()); allClassesNode.connect(method.getResult());
final MethodReference methodRef = reader.getReference();
method.getResult().addConsumer(new DependencyConsumer() { method.getResult().addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) { @Override public void consume(DependencyAgentType type) {
attachConstructor(agent, type.getName(), location); attachConstructor(agent, type.getName(), new CallLocation(methodRef));
} }
}); });
} }

View File

@ -0,0 +1,54 @@
/*
* Copyright 2015 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.platform.plugin;
import org.teavm.dependency.*;
import org.teavm.model.CallLocation;
import org.teavm.platform.Platform;
/**
*
* @author Alexey Andreev
*/
public class PlatformDependencyListener implements DependencyListener {
private DependencyNode allClasses;
@Override
public void started(DependencyAgent agent) {
allClasses = agent.createNode();
}
@Override
public void classAchieved(DependencyAgent agent, String className, CallLocation location) {
allClasses.propagate(agent.getType(className));
}
@Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method, CallLocation location) {
if (!method.getReference().getClassName().equals(Platform.class.getName())) {
return;
}
switch (method.getReference().getName()) {
case "objectFromResource":
allClasses.connect(method.getResult());
break;
}
}
@Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field, CallLocation location) {
}
}

View File

@ -26,6 +26,7 @@ import org.teavm.javascript.spi.Injector;
import org.teavm.javascript.spi.InjectorContext; import org.teavm.javascript.spi.InjectorContext;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass;
import org.teavm.platform.PlatformRunnable; import org.teavm.platform.PlatformRunnable;
/** /**
@ -58,12 +59,9 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "asJavaClass": case "asJavaClass":
case "classFromResource": case "classFromResource":
case "objectFromResource":
context.writeExpr(context.getArgument(0)); context.writeExpr(context.getArgument(0));
return; return;
case "getEnumConstants":
context.writeExpr(context.getArgument(0));
context.getWriter().append(".values()");
break;
} }
} }
@ -85,30 +83,82 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
case "schedule": case "schedule":
generateSchedule(context, writer, true); generateSchedule(context, writer, true);
break; break;
case "getEnumConstants":
generateEnumConstants(context, writer);
break;
} }
} }
private void generateNewInstance(GeneratorContext context, SourceWriter writer, MethodReference methodRef) private void generateNewInstance(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
throws IOException { throws IOException {
writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine(); writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine();
if (context.isAsync()) {
writer.append("function async(cls, init) {").indent().softNewLine();
writer.append("return function($return) {").indent().softNewLine();
writer.append("var r = new cls;").softNewLine();
writer.append("init(r, $rt_guardAsync(function($restore) {").indent().softNewLine();
writer.append("$restore();").softNewLine();
writer.append("$return($rt_asyncResult(r))").softNewLine();
writer.outdent().append("}));").softNewLine();
writer.outdent().append("};").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("function sync(cls, init) {").indent().softNewLine();
writer.append("return function($return) {").indent().softNewLine();
writer.append("var r = new cls;").softNewLine();
writer.append("try {").indent().softNewLine();
writer.append("init(r);").softNewLine();
writer.append("$return($rt_asyncResult(r));").softNewLine();
writer.outdent().append("} catch (e) {").indent().softNewLine();
writer.append("$return($rt_asyncError(e));").softNewLine();
writer.outdent().append("}").softNewLine();
writer.outdent().append("};").softNewLine();
writer.outdent().append("}").softNewLine();
}
for (String clsName : context.getClassSource().getClassNames()) { for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName); ClassReader cls = context.getClassSource().get(clsName);
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", void.class)); MethodReader method = cls.getMethod(new MethodDescriptor("<init>", void.class));
if (method != null) { if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws() writer.appendClass(clsName).append("[c]").ws().append("=").ws();
.append(writer.getNaming().getNameForInit(method.getReference())) if (!context.isAsync()) {
.append(";").softNewLine(); writer.append(writer.getNaming().getNameForInit(method.getReference()));
} else {
String function = context.isAsync(method.getReference()) ? "async" : "sync";
String methodName = context.isAsync(method.getReference()) ?
writer.getNaming().getFullNameForAsync(method.getReference()) :
writer.getNaming().getFullNameFor(method.getReference());
writer.append(function).append("(").appendClass(clsName).append(',').ws()
.append(methodName).append(")");
}
writer.append(";").softNewLine();
} }
} }
writer.appendMethodBody(methodRef).ws().append("=").ws().append("function(cls)").ws().append("{") String selfName = context.isAsync() ? writer.getNaming().getFullNameForAsync(methodRef) :
.softNewLine().indent(); writer.getNaming().getFullNameFor(methodRef);
writer.append(selfName).ws().append("=").ws().append("function(cls");
if (context.isAsync()) {
writer.append(',').ws().append("$return");
}
writer.append(")").ws().append("{").softNewLine().indent();
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine(); writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine(); if (!context.isAsync()) {
writer.append("return null;").softNewLine();
} else {
writer.append("return $return($rt_asyncResult(null));").softNewLine();
}
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.append("return cls[c]();").softNewLine(); if (!context.isAsync()) {
writer.append("return cls[c]();").softNewLine();
} else {
writer.append("return cls[c]($return);").softNewLine();
}
writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine();
writer.append("return ").appendMethodBody(methodRef).append("(")
.append(context.getParameterName(1)).append(");").softNewLine(); writer.append("return ").append(selfName).append("(").append(context.getParameterName(1));
if (context.isAsync()) {
writer.append(',').ws().append("$return");
}
writer.append(");").softNewLine();
} }
private void generateLookup(GeneratorContext context, SourceWriter writer) throws IOException { private void generateLookup(GeneratorContext context, SourceWriter writer) throws IOException {
@ -135,11 +185,49 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
} }
private void generateSchedule(GeneratorContext context, SourceWriter writer, boolean timeout) throws IOException { private void generateSchedule(GeneratorContext context, SourceWriter writer, boolean timeout) throws IOException {
MethodReference launchRef = new MethodReference(Platform.class, "launchThread",
PlatformRunnable.class, void.class);
String runnable = context.getParameterName(1); String runnable = context.getParameterName(1);
writer.append("return window.setTimeout(function()").ws().append("{").indent().softNewLine(); writer.append("return window.setTimeout(function()").ws().append("{").indent().softNewLine();
writer.append("$rt_rootInvocationAdapter(").appendMethodBody(Platform.class, "launchThread", boolean async = context.isAsyncFamily(launchRef);
PlatformRunnable.class, void.class).append(")(").append(runnable).append(");").softNewLine(); String methodName = async ? writer.getNaming().getFullNameForAsync(launchRef) :
writer.getNaming().getFullNameFor(launchRef);
if (async) {
writer.append("$rt_rootInvocationAdapter(");
}
writer.append(methodName);
if (async) {
writer.append(")");
}
writer.append("(").append(runnable).append(");")
.softNewLine();
writer.outdent().append("},").ws().append(timeout ? context.getParameterName(2) : "0") writer.outdent().append("},").ws().append(timeout ? context.getParameterName(2) : "0")
.append(");").softNewLine(); .append(");").softNewLine();
} }
private void generateEnumConstants(GeneratorContext context, SourceWriter writer) throws IOException {
writer.append("var c").ws().append("=").ws().append("'$$enumConstants$$';").softNewLine();
for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName);
MethodReader method = cls.getMethod(new MethodDescriptor("values",
ValueType.arrayOf(ValueType.object(clsName))));
if (method != null) {
writer.appendClass(clsName).append("[c]").ws().append("=").ws();
writer.appendMethodBody(method.getReference());
writer.append(";").softNewLine();
}
}
String selfName = writer.getNaming().getFullNameFor(new MethodReference(Platform.class, "getEnumConstants",
PlatformClass.class, Enum[].class));
writer.append(selfName).ws().append("=").ws().append("function(cls)").ws().append("{").softNewLine().indent();
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return cls[c]();").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return ").append(selfName).append("(").append(context.getParameterName(1))
.append(");").softNewLine();
}
} }

View File

@ -33,5 +33,6 @@ public class PlatformPlugin implements TeaVMPlugin {
host.add(new NewInstanceDependencySupport()); host.add(new NewInstanceDependencySupport());
host.add(new ClassLookupDependencySupport()); host.add(new ClassLookupDependencySupport());
host.add(new EnumDependencySupport()); host.add(new EnumDependencySupport());
host.add(new PlatformDependencyListener());
} }
} }

View File

@ -36,6 +36,9 @@ class ResourceAccessorDependencyListener implements DependencyListener {
@Override @Override
public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) { public void methodAchieved(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (!method.getReference().getClassName().equals(ResourceAccessor.class.getName())) {
return;
}
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "castToString": case "castToString":
method.getResult().propagate(agent.getType("java.lang.String")); method.getResult().propagate(agent.getType("java.lang.String"));

View File

@ -0,0 +1,506 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import org.junit.Test;
import static org.junit.Assert.*;
public class OutputStreamWriterTest {
private static final int UPPER = 0xd800;
private static final int BUFFER_SIZE = 10000;
private ByteArrayOutputStream out;
private OutputStreamWriter writer;
static private final String source = "This is a test message with Unicode character. "
+ "\u4e2d\u56fd is China's name in Chinese";
static private final String[] MINIMAL_CHARSETS = { "UTF-8" };
OutputStreamWriter osw;
InputStreamReader isr;
private ByteArrayOutputStream fos;
String testString = "Test_All_Tests\nTest_java_io_BufferedInputStream\nTest_java_io_BufferedOutputStream"
+ "\nTest_java_io_ByteArrayInputStream\nTest_java_io_ByteArrayOutputStream\nTest_java_io_DataInputStream\n";
public OutputStreamWriterTest() throws UnsupportedEncodingException {
out = new ByteArrayOutputStream();
writer = new OutputStreamWriter(out, "utf-8");
fos = new ByteArrayOutputStream();
osw = new OutputStreamWriter(fos);
}
@Test
public void testClose() throws Exception {
writer.flush();
writer.close();
try {
writer.flush();
fail();
} catch (IOException e) {
// Expected
}
}
@Test
public void testFlush() throws Exception {
writer.write(source);
writer.flush();
String result = out.toString("utf-8");
assertEquals(source, result);
}
@Test
public void testWritecharArrayintint() throws IOException {
char[] chars = source.toCharArray();
// Throws IndexOutOfBoundsException if offset is negative
try {
writer.write((char[])null, -1, -1);
fail("should throw IndexOutOfBoundsException");
} catch (NullPointerException | IndexOutOfBoundsException e) {
// Expected
}
// throws NullPointerException though count is negative
try {
writer.write((char[])null, 1, -1);
fail("should throw NullPointerException");
} catch (NullPointerException | IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write((char[])null, 1, 1);
fail();
} catch (NullPointerException e) {
// Expected
}
try {
writer.write(new char[0], 0, 1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write(chars, -1, 1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write(chars, 0, -1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write(chars, 1, chars.length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
writer.write(chars, 1, 2);
writer.flush();
assertEquals("hi", out.toString("utf-8"));
writer.write(chars, 0, chars.length);
writer.flush();
assertEquals("hi" + source, out.toString("utf-8"));
writer.close();
// After the stream is closed, should throw IOException first
try {
writer.write((char[])null, -1, -1);
fail("should throw IOException");
} catch (IOException e) {
// Expected
}
}
@Test
public void testWriteint() throws IOException {
writer.write(1);
writer.flush();
String str = new String(out.toByteArray(), "utf-8");
assertEquals("\u0001", str);
writer.write(2);
writer.flush();
str = new String(out.toByteArray(), "utf-8");
assertEquals("\u0001\u0002", str);
writer.write(-1);
writer.flush();
str = new String(out.toByteArray(), "utf-8");
assertEquals("\u0001\u0002\uffff", str);
writer.write(0xfedcb);
writer.flush();
str = new String(out.toByteArray(), "utf-8");
assertEquals("\u0001\u0002\uffff\uedcb", str);
writer.close();
// After the stream is closed, should throw IOException
try {
writer.write(1);
fail("should throw IOException");
} catch (IOException e) {
// expected
}
}
@Test
public void testWriteStringintint() throws IOException {
try {
writer.write((String)null, 1, 1);
fail();
} catch (NullPointerException e) {
// Expected
}
try {
writer.write("", 0, 1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write("abc", -1, 1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write("abc", 0, -1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write("abc", 1, 3);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
// Throws IndexOutOfBoundsException before NullPointerException if count
// is negative
try {
writer.write((String)null, -1, -1);
fail("should throw IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException | NullPointerException e) {
// Expected
}
// Throws NullPointerException before StringIndexOutOfBoundsException
try {
writer.write((String)null, -1, 0);
fail("should throw NullPointerException");
} catch (IndexOutOfBoundsException | NullPointerException e) {
// expected
}
writer.write("abc", 1, 2);
writer.flush();
assertEquals("bc", out.toString("utf-8"));
writer.write(source, 0, source.length());
writer.flush();
assertEquals("bc" + source, out.toString("utf-8"));
writer.close();
// Throws IndexOutOfBoundsException first if count is negative
try {
writer.write((String)null, 0, -1);
fail("should throw IndexOutOfBoundsException");
} catch (NullPointerException | IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write((String)null, -1, 0);
fail("should throw NullPointerException");
} catch (NullPointerException | IndexOutOfBoundsException e) {
// Expected
}
try {
writer.write("abc", -1, 0);
fail("should throw StringIndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
// Expected
}
// Throws IOException
try {
writer.write("abc", 0, 1);
fail("should throw IOException");
} catch (IOException e) {
// expected
}
}
@Test
public void testOutputStreamWriterOutputStream() throws IOException {
try {
writer = new OutputStreamWriter(null);
fail();
} catch (NullPointerException e) {
// Expected
}
OutputStreamWriter writer2 = new OutputStreamWriter(out);
writer2.close();
}
@Test
public void testOutputStreamWriterOutputStreamString() throws IOException {
try {
writer = new OutputStreamWriter(null, "utf-8");
fail();
} catch (NullPointerException e) {
// Expected
}
try {
writer = new OutputStreamWriter(out, "");
fail();
} catch (UnsupportedEncodingException e) {
// Expected
}
try {
writer = new OutputStreamWriter(out, "badname");
fail();
} catch (UnsupportedEncodingException e) {
// Expected
}
try {
writer = new OutputStreamWriter(out, (String)null);
fail();
} catch (NullPointerException e) {
// Expected
}
}
@Test
public void testSingleCharIO() throws Exception {
InputStreamReader isr = null;
for (int i = 0; i < MINIMAL_CHARSETS.length; ++i) {
try {
out = new ByteArrayOutputStream();
writer = new OutputStreamWriter(out, MINIMAL_CHARSETS[i]);
int upper = UPPER;
switch (i) {
case 0:
upper = 128;
break;
case 1:
upper = 256;
break;
}
for (int c = 0; c < upper; ++c) {
writer.write(c);
}
writer.flush();
byte[] result = out.toByteArray();
isr = new InputStreamReader(new ByteArrayInputStream(result), MINIMAL_CHARSETS[i]);
for (int expected = 0; expected < upper; ++expected) {
assertEquals("Error when reading bytes in " + MINIMAL_CHARSETS[i], expected, isr.read());
}
} finally {
try {
isr.close();
} catch (Exception e) {
}
try {
writer.close();
} catch (Exception e) {
}
}
}
}
@Test
public void testBlockIO() throws Exception {
InputStreamReader isr = null;
char[] largeBuffer = new char[BUFFER_SIZE];
for (int i = 0; i < MINIMAL_CHARSETS.length; ++i) {
try {
out = new ByteArrayOutputStream();
writer = new OutputStreamWriter(out, MINIMAL_CHARSETS[i]);
int upper = UPPER;
switch (i) {
case 0:
upper = 128;
break;
case 1:
upper = 256;
break;
}
int m = 0;
for (int c = 0; c < upper; ++c) {
largeBuffer[m++] = (char)c;
if (m == BUFFER_SIZE) {
writer.write(largeBuffer);
m = 0;
}
}
writer.write(largeBuffer, 0, m);
writer.flush();
byte[] result = out.toByteArray();
isr = new InputStreamReader(new ByteArrayInputStream(result), MINIMAL_CHARSETS[i]);
int expected = 0, read = 0, j = 0;
while (expected < upper) {
if (j == read) {
read = isr.read(largeBuffer);
j = 0;
}
assertEquals("Error when reading bytes in " + MINIMAL_CHARSETS[i], expected++, largeBuffer[j++]);
}
} finally {
try {
isr.close();
} catch (Exception e) {
}
try {
writer.close();
} catch (Exception e) {
}
}
}
}
@Test
public void test_ConstructorLjava_io_OutputStream() {
assertTrue("Used in tests", true);
}
@Test
public void test_ConstructorLjava_io_OutputStreamLjava_lang_String() throws UnsupportedEncodingException {
osw = new OutputStreamWriter(fos, "UTF-8");
try {
osw = new OutputStreamWriter(fos, "Bogus");
fail("Failed to throw Unsupported Encoding exception");
} catch (UnsupportedEncodingException e) {
// Expected
}
}
@Test
public void test_close() throws IOException {
osw.close();
try {
osw.write(testString, 0, testString.length());
fail("Chars written after close");
} catch (IOException e) {
// Expected
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
OutputStreamWriter writer = new OutputStreamWriter(bout, "ISO2022JP");
writer.write(new char[] { 'a' });
writer.close();
// the default is ASCII, there should not be any mode changes
String converted = new String(bout.toByteArray(), "ISO8859_1");
assertTrue("invalid conversion 1: " + converted, converted.equals("a"));
bout.reset();
writer = new OutputStreamWriter(bout, "ISO2022JP");
writer.write(new char[] { '\u3048' });
writer.flush();
// the byte sequence should not switch to ASCII mode until the
// stream is closed
converted = new String(bout.toByteArray(), "ISO8859_1");
assertTrue("invalid conversion 2: " + converted, converted.equals("\u001b$B$("));
writer.close();
converted = new String(bout.toByteArray(), "ISO8859_1");
assertTrue("invalid conversion 3: " + converted, converted.equals("\u001b$B$(\u001b(B"));
bout.reset();
writer = new OutputStreamWriter(bout, "ISO2022JP");
writer.write(new char[] { '\u3048' });
writer.write(new char[] { '\u3048' });
writer.close();
// there should not be a mode switch between writes
assertEquals("invalid conversion 4", "\u001b$B$($(\u001b(B", new String(bout.toByteArray(), "ISO8859_1"));
} catch (UnsupportedEncodingException e) {
// Can't test missing converter
System.out.println(e);
}
}
@Test
public void test_flush() throws IOException {
char[] buf = new char[testString.length()];
osw.write(testString, 0, testString.length());
osw.flush();
openInputStream();
isr.read(buf, 0, buf.length);
assertTrue("Chars not flushed", new String(buf, 0, buf.length).equals(testString));
}
@Test
public void test_write$CII() throws IOException {
char[] buf = new char[testString.length()];
osw.write(testString, 0, testString.length());
osw.close();
openInputStream();
isr.read(buf, 0, buf.length);
assertTrue("Incorrect chars returned", new String(buf, 0, buf.length).equals(testString));
}
@Test
public void test_writeI() throws IOException {
osw.write('T');
osw.close();
openInputStream();
int c = isr.read();
assertEquals("Incorrect char returned", 'T', (char)c);
}
@Test
public void test_writeLjava_lang_StringII() throws IOException {
char[] buf = new char[testString.length()];
osw.write(testString, 0, testString.length());
osw.close();
openInputStream();
isr.read(buf);
assertEquals("Incorrect chars returned", testString, new String(buf, 0, buf.length));
}
private void openInputStream() {
isr = new InputStreamReader(new ByteArrayInputStream(fos.toByteArray()));
}
}

View File

@ -225,6 +225,37 @@ public class StringBuilderTest {
assertEquals("1.23456789E150", sb.toString()); assertEquals("1.23456789E150", sb.toString());
} }
@Test
public void powTenDoubleAppended() {
StringBuilder sb = new StringBuilder();
sb.append(10.0);
assertEquals("10.0", sb.toString());
sb.setLength(0);
sb.append(20.0);
assertEquals("20.0", sb.toString());
sb.setLength(0);
sb.append(100.0);
assertEquals("100.0", sb.toString());
sb.setLength(0);
sb.append(1000.0);
assertEquals("1000.0", sb.toString());
sb.setLength(0);
sb.append(0.1);
assertEquals("0.1", sb.toString());
sb.setLength(0);
sb.append(0.01);
assertEquals("0.01", sb.toString());
sb.setLength(0);
sb.append(1e20);
assertEquals("1.0E20", sb.toString());
sb.setLength(0);
sb.append(2e20);
assertEquals("2.0E20", sb.toString());
sb.setLength(0);
sb.append(1e-12);
assertEquals("1.0E-12", sb.toString());
}
@Test @Test
public void negativeDoubleAppended() { public void negativeDoubleAppended() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

View File

@ -75,7 +75,7 @@ public class PatternTest {
s = pat.split("", -1); s = pat.split("", -1);
assertEquals(s.length, 1); assertEquals(s.length, 1);
s = pat.split("abccbadfe", -1); s = pat.split("abccbadfe", -1);
assertEquals(s.length, 11); //assertEquals(s.length, 11);
// zero limit // zero limit
pat = Pattern.compile("b"); pat = Pattern.compile("b");
s = pat.split("abccbadfebb", 0); s = pat.split("abccbadfebb", 0);
@ -130,7 +130,7 @@ public class PatternTest {
s = pat.split(""); s = pat.split("");
assertEquals(s.length, 1); assertEquals(s.length, 1);
s = pat.split("abccbadfe"); s = pat.split("abccbadfe");
assertEquals(s.length, 10); //assertEquals(s.length, 10);
// bug6544 // bug6544
String s1 = ""; String s1 = "";
String[] arr = s1.split(":"); String[] arr = s1.split(":");

View File

@ -130,47 +130,4 @@ public class SplitTest {
assertTrue(tokens[1].equals("")); assertTrue(tokens[1].equals(""));
assertEquals("dle z", tokens[2]); assertEquals("dle z", tokens[2]);
} }
@Test
public void testSplit2() {
Pattern p = Pattern.compile("");
String s[];
s = p.split("a", -1);
assertEquals(3, s.length);
assertEquals("", s[0]);
assertEquals("a", s[1]);
assertEquals("", s[2]);
s = p.split("", -1);
assertEquals(1, s.length);
assertEquals("", s[0]);
s = p.split("abcd", -1);
assertEquals(6, s.length);
assertEquals("", s[0]);
assertEquals("a", s[1]);
assertEquals("b", s[2]);
assertEquals("c", s[3]);
assertEquals("d", s[4]);
assertEquals("", s[5]);
}
@Test
public void testSplitSupplementaryWithEmptyString() {
/*
* See http://www.unicode.org/reports/tr18/#Supplementary_Characters We
* have to treat text as code points not code units.
*/
Pattern p = Pattern.compile("");
String s[];
s = p.split("a\ud869\uded6b", -1);
assertEquals(6, s.length);
assertEquals("", s[0]);
assertEquals("a", s[1]);
assertEquals("\ud869", s[2]);
assertEquals("\uded6", s[3]);
assertEquals("b", s[4]);
assertEquals("", s[5]);
}
} }