C: implement native file system

This commit is contained in:
Alexey Andreev 2019-05-29 17:49:34 +03:00
parent 6c9393548a
commit 91de1f6ca7
39 changed files with 1579 additions and 213 deletions

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.classlib.fs; package org.teavm.classlib.fs;
import java.io.IOException;
public interface VirtualFile { public interface VirtualFile {
String getName(); String getName();
@ -22,19 +24,17 @@ public interface VirtualFile {
boolean isFile(); boolean isFile();
VirtualFile[] listFiles(); String[] listFiles();
VirtualFile getChildFile(String fileName); VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append);
VirtualFileAccessor createAccessor(); boolean createFile(String fileName) throws IOException;
VirtualFile createFile(String fileName); boolean createDirectory(String fileName);
VirtualFile createDirectory(String fileName); boolean delete();
void delete(); boolean adopt(VirtualFile file, String fileName);
void adopt(VirtualFile targetDir, String fileName);
boolean canRead(); boolean canRead();
@ -42,9 +42,9 @@ public interface VirtualFile {
long lastModified(); long lastModified();
void setLastModified(long lastModified); boolean setLastModified(long lastModified);
void setReadOnly(boolean readOnly); boolean setReadOnly(boolean readOnly);
int length(); int length();
} }

View File

@ -22,7 +22,11 @@ public interface VirtualFileAccessor {
void write(int pos, byte[] buffer, int offset, int limit) throws IOException; void write(int pos, byte[] buffer, int offset, int limit) throws IOException;
int size(); int size() throws IOException;
void resize(int size) throws IOException; void resize(int size) throws IOException;
void close() throws IOException;
void flush() throws IOException;
} }

View File

@ -16,7 +16,7 @@
package org.teavm.classlib.fs; package org.teavm.classlib.fs;
public interface VirtualFileSystem { public interface VirtualFileSystem {
VirtualFile getRootFile();
String getUserDir(); String getUserDir();
VirtualFile getFile(String path);
} }

View File

@ -15,8 +15,20 @@
*/ */
package org.teavm.classlib.fs; package org.teavm.classlib.fs;
import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.fs.c.CFileSystem;
import org.teavm.classlib.fs.memory.InMemoryVirtualFileSystem;
public final class VirtualFileSystemProvider { public final class VirtualFileSystemProvider {
private static VirtualFileSystem instance = new InMemoryVirtualFileSystem(); private static VirtualFileSystem instance;
static {
if (PlatformDetector.isC()) {
instance = new CFileSystem();
} else {
instance = new InMemoryVirtualFileSystem();
}
}
private VirtualFileSystemProvider() { private VirtualFileSystemProvider() {
} }

View File

@ -0,0 +1,173 @@
/*
* Copyright 2019 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.fs.c;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import org.teavm.classlib.fs.VirtualFile;
import org.teavm.classlib.fs.VirtualFileSystem;
import org.teavm.classlib.impl.c.Memory;
import org.teavm.classlib.impl.c.StringList;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
public class CFileSystem implements VirtualFileSystem {
private Map<String, Entry> cache = new HashMap<>();
private ReferenceQueue<? super CVirtualFile> referenceQueue = new ReferenceQueue<>();
@Override
public String getUserDir() {
Address resultPtr = Memory.malloc(Address.sizeOf());
int length = workDirectory(resultPtr);
Address result = resultPtr.getAddress();
Memory.free(resultPtr);
int realLength = length > 0 ? length : -length - 1;
char[] chars = new char[realLength];
Memory.memcpy(Address.ofData(chars), result, chars.length * 2);
Memory.free(result);
String s = new String(chars);
if (length < 0) {
throw new RuntimeException(s);
} else {
return s;
}
}
@Override
public VirtualFile getFile(String path) {
return getByPath(path);
}
CVirtualFile getByPath(String path) {
Entry entry = cache.get(path);
if (entry == null || entry.get() == null) {
entry = new Entry(new CVirtualFile(this, path), referenceQueue);
cache.put(path, entry);
}
while (true) {
Entry staleEntry = (Entry) referenceQueue.poll();
if (staleEntry == null) {
break;
}
if (!staleEntry.path.equals(path)) {
cache.remove(staleEntry.path);
}
}
return entry.get();
}
static class Entry extends WeakReference<CVirtualFile> {
String path;
Entry(CVirtualFile referent, ReferenceQueue<? super CVirtualFile> q) {
super(referent, q);
this.path = referent.path;
}
}
@Import(name = "teavm_file_homeDirectory")
@Unmanaged
public static native int homeDirectory(Address resultPtr);
@Import(name = "teavm_file_workDirectory")
@Unmanaged
static native int workDirectory(Address resultPtr);
@Import(name = "teavm_file_isDir")
@Unmanaged
static native boolean isDir(char[] name, int nameSize);
@Import(name = "teavm_file_isFile")
@Unmanaged
static native boolean isFile(char[] name, int nameSize);
@Import(name = "teavm_file_canRead")
@Unmanaged
static native boolean canRead(char[] name, int nameSize);
@Import(name = "teavm_file_canWrite")
@Unmanaged
static native boolean canWrite(char[] name, int nameSize);
@Import(name = "teavm_file_setReadonly")
@Unmanaged
static native boolean setReadonly(char[] name, int nameSize, boolean readonly);
@Import(name = "teavm_file_listFiles")
@Unmanaged
static native StringList listFiles(char[] name, int nameSize);
@Import(name = "teavm_file_createDirectory")
@Unmanaged
static native boolean createDirectory(char[] name, int nameSize);
@Import(name = "teavm_file_createFile")
@Unmanaged
static native int createFile(char[] name, int nameSize);
@Import(name = "teavm_file_delete")
@Unmanaged
static native boolean delete(char[] name, int nameSize);
@Import(name = "teavm_file_rename")
@Unmanaged
static native boolean rename(char[] name, int nameSize, char[] newName, int newNameSize);
@Import(name = "teavm_file_length")
@Unmanaged
static native int length(char[] name, int nameSize);
@Import(name = "teavm_file_lastModified")
@Unmanaged
static native long lastModified(char[] name, int nameSize);
@Import(name = "teavm_file_setLastModified")
@Unmanaged
static native boolean setLastModified(char[] name, int nameSize, long lastModified);
@Import(name = "teavm_file_open")
@Unmanaged
static native long open(char[] name, int nameSize, int mode);
@Import(name = "teavm_file_close")
@Unmanaged
static native boolean close(long file);
@Import(name = "teavm_file_flush")
@Unmanaged
static native boolean flush(long file);
@Import(name = "teavm_file_seek")
@Unmanaged
static native boolean seek(long file, int where, int offset);
@Import(name = "teavm_file_tell")
@Unmanaged
static native int tell(long file);
@Import(name = "teavm_file_read")
@Unmanaged
static native int read(long file, byte[] data, int offset, int count);
@Import(name = "teavm_file_write")
@Unmanaged
static native int write(long file, byte[] data, int offset, int count);
}

View File

@ -0,0 +1,180 @@
/*
* Copyright 2019 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.fs.c;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.teavm.classlib.fs.VirtualFile;
import org.teavm.classlib.fs.VirtualFileAccessor;
import org.teavm.classlib.impl.c.Memory;
import org.teavm.classlib.impl.c.StringList;
import org.teavm.interop.Address;
public class CVirtualFile implements VirtualFile {
CFileSystem fileSystem;
String path;
public CVirtualFile(CFileSystem fileSystem, String path) {
this.fileSystem = fileSystem;
this.path = path;
}
@Override
public String getName() {
return path.substring(path.lastIndexOf('/') + 1);
}
@Override
public boolean isDirectory() {
char[] chars = path.toCharArray();
return CFileSystem.isDir(chars, chars.length);
}
@Override
public boolean isFile() {
char[] chars = path.toCharArray();
return CFileSystem.isFile(chars, chars.length);
}
@Override
public String[] listFiles() {
char[] chars = path.toCharArray();
StringList list = CFileSystem.listFiles(chars, chars.length);
if (list == null) {
return null;
}
List<String> files = new ArrayList<>();
while (list != null) {
if (list.data != null) {
chars = new char[list.length];
Address data = list.data.toAddress();
for (int i = 0; i < chars.length; ++i) {
chars[i] = data.add(i * 2).getChar();
}
Memory.free(data);
String name = new String(chars);
if (!name.equals(".") && !name.equals("..")) {
files.add(name);
}
}
StringList next = list.next;
Memory.free(list.toAddress());
list = next;
}
Collections.reverse(files);
return files.toArray(new String[0]);
}
@Override
public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
char[] chars = path.toCharArray();
int mode = 0;
if (readable) {
mode |= 1;
}
if (writable) {
mode |= 2;
}
if (append) {
mode |= 4;
}
long file = CFileSystem.open(chars, chars.length, mode);
if (file == 0) {
return null;
}
return new CVirtualFileAccessor(file, append ? -1 : 0);
}
@Override
public boolean createFile(String fileName) throws IOException {
String newPath = constructPath(path, fileName);
char[] chars = newPath.toCharArray();
int result = CFileSystem.createFile(chars, chars.length);
switch (result) {
case 0:
return true;
case 1:
return false;
default:
throw new IOException("Could not create file " + fileName);
}
}
@Override
public boolean createDirectory(String fileName) {
String newPath = constructPath(path, fileName);
char[] chars = newPath.toCharArray();
return CFileSystem.createDirectory(chars, chars.length);
}
@Override
public boolean delete() {
char[] chars = path.toCharArray();
return CFileSystem.delete(chars, chars.length);
}
@Override
public boolean adopt(VirtualFile file, String fileName) {
char[] chars = ((CVirtualFile) file).path.toCharArray();
String newPath = constructPath(path, fileName);
char[] newPathChars = newPath.toCharArray();
return CFileSystem.rename(chars, chars.length, newPathChars, newPathChars.length);
}
@Override
public boolean canRead() {
char[] chars = path.toCharArray();
return CFileSystem.canRead(chars, chars.length);
}
@Override
public boolean canWrite() {
char[] chars = path.toCharArray();
return CFileSystem.canWrite(chars, chars.length);
}
@Override
public long lastModified() {
char[] chars = path.toCharArray();
return CFileSystem.lastModified(chars, chars.length);
}
@Override
public boolean setLastModified(long lastModified) {
char[] chars = path.toCharArray();
return CFileSystem.setLastModified(chars, chars.length, lastModified);
}
@Override
public boolean setReadOnly(boolean readOnly) {
char[] chars = path.toCharArray();
return CFileSystem.setReadonly(chars, chars.length, readOnly);
}
@Override
public int length() {
char[] chars = path.toCharArray();
return CFileSystem.length(chars, chars.length);
}
private static String constructPath(String parent, String child) {
return parent.endsWith("/") ? parent + child : parent + "/" + child;
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2019 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.fs.c;
import java.io.IOException;
import org.teavm.classlib.fs.VirtualFileAccessor;
public class CVirtualFileAccessor implements VirtualFileAccessor {
private long file;
private int position;
public CVirtualFileAccessor(long file, int position) {
this.file = file;
this.position = position;
}
@Override
public int read(int pos, byte[] buffer, int offset, int limit) throws IOException {
ensurePosition(pos);
int bytesRead = CFileSystem.read(file, buffer, offset, limit);
position += bytesRead;
return bytesRead;
}
@Override
public void write(int pos, byte[] buffer, int offset, int limit) throws IOException {
ensurePosition(pos);
int bytesWritten = CFileSystem.write(file, buffer, offset, limit);
if (bytesWritten < limit) {
throw new IOException();
}
position += bytesWritten;
}
@Override
public int size() throws IOException {
if (!CFileSystem.seek(file, 2, 0)) {
throw new IOException();
}
position = CFileSystem.tell(file);
return position;
}
@Override
public void resize(int size) throws IOException {
if (!CFileSystem.seek(file, 2, 0)) {
throw new IOException();
}
position = CFileSystem.tell(file);
if (position < size) {
byte[] zeros = new byte[4096];
while (position < size) {
write(position, zeros, 0, Math.min(zeros.length, size - position));
}
}
}
@Override
public void close() throws IOException {
long file = this.file;
this.file = 0;
if (!CFileSystem.close(file)) {
throw new IOException();
}
}
@Override
public void flush() throws IOException {
if (!CFileSystem.flush(file)) {
throw new IOException();
}
}
private void ensurePosition(int pos) throws IOException {
if (position != pos) {
if (!CFileSystem.seek(file, 0, pos)) {
throw new IOException();
}
position = pos;
}
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 Alexey Andreev. * Copyright 2019 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.teavm.classlib.fs; package org.teavm.classlib.fs.memory;
public abstract class AbstractInMemoryVirtualFile implements VirtualFile { import java.io.IOException;
import org.teavm.classlib.fs.VirtualFileAccessor;
public abstract class AbstractInMemoryVirtualFile {
String name; String name;
InMemoryVirtualDirectory parent; InMemoryVirtualDirectory parent;
long lastModified = System.currentTimeMillis(); long lastModified = System.currentTimeMillis();
@ -25,44 +28,71 @@ public abstract class AbstractInMemoryVirtualFile implements VirtualFile {
this.name = name; this.name = name;
} }
@Override
public String getName() { public String getName() {
return name; return name;
} }
@Override public boolean delete() {
public void delete() { if (parent == null || (isDirectory() && listFiles().length > 0)) {
return false;
}
if (parent != null && !parent.canWrite()) {
return false;
}
parent.children.remove(name); parent.children.remove(name);
parent.modify(); parent.modify();
parent = null; parent = null;
return true;
} }
@Override public abstract boolean isDirectory();
public abstract boolean isFile();
public abstract String[] listFiles();
public boolean canRead() { public boolean canRead() {
return true; return true;
} }
@Override
public boolean canWrite() { public boolean canWrite() {
return !readOnly; return !readOnly;
} }
@Override
public long lastModified() { public long lastModified() {
return lastModified; return lastModified;
} }
@Override public boolean setLastModified(long lastModified) {
public void setLastModified(long lastModified) { if (readOnly) {
return false;
}
this.lastModified = lastModified; this.lastModified = lastModified;
return true;
} }
@Override public boolean setReadOnly(boolean readOnly) {
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly; this.readOnly = readOnly;
return true;
} }
void modify() { void modify() {
lastModified = System.currentTimeMillis(); lastModified = System.currentTimeMillis();
} }
public abstract AbstractInMemoryVirtualFile getChildFile(String fileName);
public abstract VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append);
public abstract InMemoryVirtualFile createFile(String fileName) throws IOException;
public abstract InMemoryVirtualDirectory createDirectory(String fileName);
public abstract boolean adopt(AbstractInMemoryVirtualFile file, String fileName);
public int length() {
return 0;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 Alexey Andreev. * Copyright 2019 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.teavm.classlib.fs; package org.teavm.classlib.fs.memory;
import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.teavm.classlib.fs.VirtualFileAccessor;
public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile { public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile {
final Map<String, VirtualFile> children = new LinkedHashMap<>(); final Map<String, AbstractInMemoryVirtualFile> children = new LinkedHashMap<>();
InMemoryVirtualDirectory(String name) { InMemoryVirtualDirectory(String name) {
super(name); super(name);
@ -36,46 +38,61 @@ public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile {
} }
@Override @Override
public VirtualFile[] listFiles() { public String[] listFiles() {
return children.values().toArray(new VirtualFile[0]); return children.keySet().toArray(new String[0]);
} }
@Override @Override
public VirtualFile getChildFile(String fileName) { public AbstractInMemoryVirtualFile getChildFile(String fileName) {
return children.get(fileName); return children.get(fileName);
} }
@Override @Override
public VirtualFileAccessor createAccessor() { public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
throw new UnsupportedOperationException(); return null;
} }
@Override @Override
public VirtualFile createFile(String fileName) { public InMemoryVirtualFile createFile(String fileName) throws IOException {
if (!canWrite()) {
throw new IOException("Directory is read-only");
}
if (children.containsKey(fileName)) {
return null;
}
InMemoryVirtualFile file = new InMemoryVirtualFile(fileName); InMemoryVirtualFile file = new InMemoryVirtualFile(fileName);
adoptFile(file); adoptFile(file);
return file; return file;
} }
@Override @Override
public VirtualFile createDirectory(String fileName) { public InMemoryVirtualDirectory createDirectory(String fileName) {
if (!canWrite() || getChildFile(fileName) != null) {
return null;
}
InMemoryVirtualDirectory file = new InMemoryVirtualDirectory(fileName); InMemoryVirtualDirectory file = new InMemoryVirtualDirectory(fileName);
adoptFile(file); adoptFile(file);
return file; return file;
} }
@Override @Override
public void adopt(VirtualFile file, String fileName) { public boolean adopt(AbstractInMemoryVirtualFile file, String fileName) {
AbstractInMemoryVirtualFile typedFile = (AbstractInMemoryVirtualFile) file; if (!canWrite()) {
typedFile.parent.children.remove(typedFile.name); return false;
typedFile.parent = this; }
children.put(fileName, typedFile); if (!file.parent.canWrite()) {
typedFile.name = fileName; return false;
}
file.parent.children.remove(file.name);
file.parent = this;
children.put(fileName, file);
file.name = fileName;
return true;
} }
@Override @Override
public int length() { public int length() {
throw new UnsupportedOperationException(); return 0;
} }
private void adoptFile(AbstractInMemoryVirtualFile file) { private void adoptFile(AbstractInMemoryVirtualFile file) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 Alexey Andreev. * Copyright 2019 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.teavm.classlib.fs; package org.teavm.classlib.fs.memory;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import org.teavm.classlib.fs.VirtualFileAccessor;
public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile { public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
byte[] data = new byte[0]; byte[] data = new byte[0];
@ -37,31 +38,31 @@ public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
} }
@Override @Override
public VirtualFile[] listFiles() { public String[] listFiles() {
return null;
}
@Override
public AbstractInMemoryVirtualFile getChildFile(String fileName) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public VirtualFile getChildFile(String fileName) { public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
throw new UnsupportedOperationException();
}
@Override
public VirtualFileAccessor createAccessor() {
if (parent == null) { if (parent == null) {
return null; return null;
} }
return new VirtualFileAccessor() { return new VirtualFileAccessor() {
@Override @Override
public int read(int pos, byte[] buffer, int offset, int limit) throws IOException { public int read(int pos, byte[] buffer, int offset, int limit) {
limit = Math.max(0, Math.min(size - pos, limit)); limit = Math.max(0, Math.min(size - pos, limit));
System.arraycopy(data, pos, buffer, offset, limit); System.arraycopy(data, pos, buffer, offset, limit);
return limit; return limit;
} }
@Override @Override
public void write(int pos, byte[] buffer, int offset, int limit) throws IOException { public void write(int pos, byte[] buffer, int offset, int limit) {
expandData(pos + limit); expandData(pos + limit);
System.arraycopy(buffer, offset, data, pos, limit); System.arraycopy(buffer, offset, data, pos, limit);
size = pos + limit; size = pos + limit;
@ -74,27 +75,35 @@ public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
} }
@Override @Override
public void resize(int size) throws IOException { public void resize(int size) {
expandData(size); expandData(size);
InMemoryVirtualFile.this.size = size; InMemoryVirtualFile.this.size = size;
modify(); modify();
} }
@Override
public void close() {
}
@Override
public void flush() {
}
}; };
} }
@Override @Override
public VirtualFile createFile(String fileName) { public InMemoryVirtualFile createFile(String fileName) throws IOException {
throw new IOException("Can't create file " + fileName + " since parent path denotes regular file");
}
@Override
public InMemoryVirtualDirectory createDirectory(String fileName) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public VirtualFile createDirectory(String fileName) { public boolean adopt(AbstractInMemoryVirtualFile file, String fileName) {
throw new UnsupportedOperationException(); return false;
}
@Override
public void adopt(VirtualFile file, String fileName) {
throw new UnsupportedOperationException();
} }
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 Alexey Andreev. * Copyright 2019 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,15 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.teavm.classlib.fs; package org.teavm.classlib.fs.memory;
import org.teavm.classlib.fs.VirtualFile;
import org.teavm.classlib.fs.VirtualFileSystem;
public class InMemoryVirtualFileSystem implements VirtualFileSystem { public class InMemoryVirtualFileSystem implements VirtualFileSystem {
private InMemoryVirtualDirectory root = new InMemoryVirtualDirectory(""); InMemoryVirtualDirectory root = new InMemoryVirtualDirectory("");
private String userDir = "/"; private String userDir = "/";
@Override @Override
public VirtualFile getRootFile() { public VirtualFile getFile(String path) {
return root; return new VirtualFileImpl(this, path);
} }
@Override @Override

View File

@ -0,0 +1,153 @@
/*
* Copyright 2019 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.fs.memory;
import java.io.IOException;
import org.teavm.classlib.fs.VirtualFile;
import org.teavm.classlib.fs.VirtualFileAccessor;
public class VirtualFileImpl implements VirtualFile {
private InMemoryVirtualFileSystem fs;
private String path;
public VirtualFileImpl(InMemoryVirtualFileSystem fs, String path) {
this.fs = fs;
this.path = path;
}
@Override
public String getName() {
return path.substring(path.lastIndexOf('/') + 1);
}
@Override
public boolean isDirectory() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.isDirectory();
}
@Override
public boolean isFile() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.isFile();
}
@Override
public String[] listFiles() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null ? inMemory.listFiles() : null;
}
@Override
public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null ? inMemory.createAccessor(readable, writable, append) : null;
}
@Override
public boolean createFile(String fileName) throws IOException {
AbstractInMemoryVirtualFile inMemory = findInMemory();
if (inMemory == null) {
throw new IOException("Directory does not exist");
}
return inMemory.createFile(fileName) != null;
}
@Override
public boolean createDirectory(String fileName) {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.createDirectory(fileName) != null;
}
@Override
public boolean delete() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.delete();
}
@Override
public boolean adopt(VirtualFile file, String fileName) {
AbstractInMemoryVirtualFile inMemory = findInMemory();
if (inMemory == null) {
return false;
}
AbstractInMemoryVirtualFile fileInMemory = ((VirtualFileImpl) file).findInMemory();
if (fileInMemory == null) {
return false;
}
return inMemory.adopt(fileInMemory, fileName);
}
@Override
public boolean canRead() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.canRead();
}
@Override
public boolean canWrite() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.canWrite();
}
@Override
public long lastModified() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null ? inMemory.lastModified() : 0;
}
@Override
public boolean setLastModified(long lastModified) {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.setLastModified(lastModified);
}
@Override
public boolean setReadOnly(boolean readOnly) {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null && inMemory.setReadOnly(readOnly);
}
@Override
public int length() {
AbstractInMemoryVirtualFile inMemory = findInMemory();
return inMemory != null ? inMemory.length() : 0;
}
AbstractInMemoryVirtualFile findInMemory() {
AbstractInMemoryVirtualFile file = fs.root;
int i = 0;
if (path.startsWith("/")) {
i++;
}
while (i < path.length()) {
int next = path.indexOf('/', i);
if (next < 0) {
next = path.length();
}
file = file.getChildFile(path.substring(i, next));
if (file == null) {
break;
}
i = next + 1;
}
return file;
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 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.c;
import org.teavm.interop.Structure;
public class CharPtr extends Structure {
public char value;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2019 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.c;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.interop.c.Include;
public final class Memory {
private Memory() {
}
@Include("stdlib.h")
@Import(name = "malloc")
@Unmanaged
public static native Address malloc(int size);
@Include("stdlib.h")
@Import(name = "free")
@Unmanaged
public static native void free(Address address);
@Include("string.h")
@Import(name = "memcpy")
@Unmanaged
public static native void memcpy(Address target, Address source, int size);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2019 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.c;
import org.teavm.interop.Structure;
public class StringList extends Structure {
public CharPtr data;
public int length;
public StringList next;
}

View File

@ -27,6 +27,7 @@ import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.impl.Base46; import org.teavm.classlib.impl.Base46;
import org.teavm.classlib.impl.CharFlow; import org.teavm.classlib.impl.CharFlow;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
import org.teavm.platform.metadata.ResourceMap; import org.teavm.platform.metadata.ResourceMap;
@ -190,6 +191,7 @@ public final class DateTimeZoneProvider {
@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();") @JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
@Import(module = "teavm", name = "getNativeOffset") @Import(module = "teavm", name = "getNativeOffset")
@Unmanaged
private static native int getNativeOffset(double instant); private static native int getNativeOffset(double instant);
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource(); private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();

View File

@ -258,16 +258,10 @@ public class TFile implements Serializable, Comparable<TFile> {
public String[] list() { public String[] list() {
VirtualFile virtualFile = findVirtualFile(); VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null || !virtualFile.isDirectory()) { if (virtualFile == null) {
return null; return null;
} }
VirtualFile[] entries = virtualFile.listFiles(); return virtualFile.listFiles();
String[] names = new String[entries.length];
for (int i = 0; i < entries.length; ++i) {
names[i] = entries[i].getName();
}
return names;
} }
public String[] list(TFilenameFilter filter) { public String[] list(TFilenameFilter filter) {
@ -289,13 +283,16 @@ public class TFile implements Serializable, Comparable<TFile> {
public TFile[] listFiles() { public TFile[] listFiles() {
VirtualFile virtualFile = findVirtualFile(); VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null || !virtualFile.isDirectory()) { if (virtualFile == null) {
return null;
}
String[] entries = virtualFile.listFiles();
if (entries == null) {
return null; return null;
} }
VirtualFile[] entries = virtualFile.listFiles();
TFile[] files = new TFile[entries.length]; TFile[] files = new TFile[entries.length];
for (int i = 0; i < entries.length; ++i) { for (int i = 0; i < entries.length; ++i) {
files[i] = new TFile(this, entries[i].getName()); files[i] = new TFile(this, entries[i]);
} }
return files; return files;
@ -336,7 +333,11 @@ public class TFile implements Serializable, Comparable<TFile> {
} }
public boolean exists() { public boolean exists() {
return findVirtualFile() != null; VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null) {
return false;
}
return virtualFile.isDirectory() || virtualFile.isFile();
} }
public long lastModified() { public long lastModified() {
@ -349,21 +350,24 @@ public class TFile implements Serializable, Comparable<TFile> {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
VirtualFile file = findVirtualFile(); VirtualFile file = findVirtualFile();
if (file == null || !file.canWrite()) { if (file == null) {
return false; return false;
} }
file.setLastModified(time); return file.setLastModified(time);
return true;
} }
public boolean setReadOnly() { public boolean setReadOnly() {
VirtualFile file = findVirtualFile(); VirtualFile file = findVirtualFile();
if (file == null || !file.canWrite()) { if (file == null) {
return false; return false;
} }
file.setReadOnly(true); return file.setReadOnly(true);
return true; }
public boolean setWritable(boolean writable) {
VirtualFile file = findVirtualFile();
return file.setReadOnly(!writable);
} }
public long length() { public long length() {
@ -376,51 +380,35 @@ public class TFile implements Serializable, Comparable<TFile> {
if (parentVirtualFile == null) { if (parentVirtualFile == null) {
throw new IOException("Can't create file " + getPath() + " since parent directory does not exist"); throw new IOException("Can't create file " + getPath() + " since parent directory does not exist");
} }
if (!parentVirtualFile.isDirectory() || !parentVirtualFile.canWrite()) {
throw new IOException("Can't create file " + getPath() + " since parent path denotes regular file");
}
if (parentVirtualFile.getChildFile(getName()) != null) { return parentVirtualFile.createFile(getName());
return false;
}
return parentVirtualFile.createFile(getName()) != null;
} }
public boolean mkdir() { public boolean mkdir() {
VirtualFile virtualFile = findParentFile(); VirtualFile virtualFile = findParentFile();
if (virtualFile == null || !virtualFile.isDirectory() || !virtualFile.canWrite()) { return virtualFile != null && virtualFile.createDirectory(getName());
return false;
}
return virtualFile.createDirectory(getName()) != null;
} }
public boolean mkdirs() { public boolean mkdirs() {
String path = getCanonicalPathImpl(); String path = getCanonicalPathImpl();
if (path.startsWith("/")) {
path = path.substring(1);
}
VirtualFile virtualFile = fs().getRootFile(); int i = path.indexOf('/');
int i = 0; if (i < 0) {
return false;
}
i++;
while (i < path.length()) { while (i < path.length()) {
int next = path.indexOf('/', i); int next = path.indexOf('/', i);
if (next < 0) { if (next < 0) {
next = path.length(); next = path.length();
} }
String name = path.substring(i, next); VirtualFile parent = fs().getFile(path.substring(0, i));
VirtualFile child = virtualFile.getChildFile(name); if (!parent.createDirectory(path.substring(i, next))) {
if (child == null) { VirtualFile child = fs().getFile(path.substring(0, next));
if (!virtualFile.canWrite()) { if (!child.isDirectory()) {
return false; return false;
} }
virtualFile = virtualFile.createDirectory(name);
} else if (child.isFile()) {
return false;
} else {
virtualFile = child;
} }
i = next + 1; i = next + 1;
@ -431,18 +419,7 @@ public class TFile implements Serializable, Comparable<TFile> {
public boolean delete() { public boolean delete() {
VirtualFile virtualFile = findVirtualFile(); VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null || virtualFile == fs().getRootFile() return virtualFile.delete();
|| (virtualFile.isDirectory() && virtualFile.listFiles().length > 0)) {
return false;
}
VirtualFile parentVirtualFile = findParentFile();
if (parentVirtualFile != null && !parentVirtualFile.canWrite()) {
return false;
}
virtualFile.delete();
return true;
} }
public void deleteOnExit() { public void deleteOnExit() {
@ -451,7 +428,7 @@ public class TFile implements Serializable, Comparable<TFile> {
public boolean renameTo(TFile dest) { public boolean renameTo(TFile dest) {
VirtualFile targetDir = dest.findParentFile(); VirtualFile targetDir = dest.findParentFile();
if (targetDir == null || !targetDir.isDirectory()) { if (targetDir == null) {
return false; return false;
} }
@ -460,8 +437,7 @@ public class TFile implements Serializable, Comparable<TFile> {
return false; return false;
} }
targetDir.adopt(virtualFile, dest.getName()); return targetDir.adopt(virtualFile, dest.getName());
return true;
} }
public URI toURI() { public URI toURI() {
@ -589,7 +565,7 @@ public class TFile implements Serializable, Comparable<TFile> {
separatorIndex++; separatorIndex++;
} }
if (separatorIndex > 0) { if (separatorIndex > 0) {
name = name.substring(separatorIndex, name.length()); name = name.substring(separatorIndex);
} }
if (!dirPath.isEmpty() && dirPath.charAt(dirPath.length() - 1) == separatorChar) { if (!dirPath.isEmpty() && dirPath.charAt(dirPath.length() - 1) == separatorChar) {
@ -602,28 +578,7 @@ public class TFile implements Serializable, Comparable<TFile> {
} }
VirtualFile findVirtualFile() { VirtualFile findVirtualFile() {
String path = getCanonicalPathImpl(); return fs().getFile(getCanonicalPathImpl());
if (path.startsWith("/")) {
path = path.substring(1);
}
VirtualFile virtualFile = fs().getRootFile();
int i = 0;
while (i < path.length()) {
int next = path.indexOf('/', i);
if (next < 0) {
next = path.length();
}
virtualFile = virtualFile.getChildFile(path.substring(i, next));
if (virtualFile == null) {
return null;
}
i = next + 1;
}
return virtualFile;
} }
VirtualFile findParentFile() { VirtualFile findParentFile() {
@ -631,6 +586,6 @@ public class TFile implements Serializable, Comparable<TFile> {
if (path.isEmpty() || path.equals("/")) { if (path.isEmpty() || path.equals("/")) {
return null; return null;
} }
return new TFile(getCanonicalPathImpl()).getParentFile().findVirtualFile(); return new TFile(path).getParentFile().findVirtualFile();
} }
} }

View File

@ -33,7 +33,7 @@ public class TFileInputStream extends InputStream {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
accessor = virtualFile.createAccessor(); accessor = virtualFile.createAccessor(true, false, false);
if (accessor == null) { if (accessor == null) {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }

View File

@ -43,28 +43,23 @@ public class TFileOutputStream extends OutputStream {
if (file.getName().isEmpty()) { if (file.getName().isEmpty()) {
throw new FileNotFoundException("Invalid file name"); throw new FileNotFoundException("Invalid file name");
} }
VirtualFile virtualFile = file.findVirtualFile();
if (virtualFile == null) {
VirtualFile parentVirtualFile = file.findParentFile(); VirtualFile parentVirtualFile = file.findParentFile();
if (parentVirtualFile != null && parentVirtualFile.isDirectory()) { if (parentVirtualFile != null) {
virtualFile = parentVirtualFile.createFile(file.getName()); try {
} parentVirtualFile.createFile(file.getName());
} } catch (IOException e) {
if (virtualFile == null || virtualFile.isDirectory()) {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
if (!virtualFile.canWrite()) {
throw new FileNotFoundException("File is read-only");
} }
accessor = virtualFile.createAccessor(); VirtualFile virtualFile = file.findVirtualFile();
accessor = virtualFile.createAccessor(false, true, append);
if (accessor == null) { if (accessor == null) {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
if (append) { if (append) {
pos = accessor.size(); pos = -1;
} }
} }
@ -74,23 +69,28 @@ public class TFileOutputStream extends OutputStream {
if (off < 0 || len < 0 || off > b.length - len) { if (off < 0 || len < 0 || off > b.length - len) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
ensureOpened(); ensurePos();
accessor.write(pos, b, off, len); accessor.write(pos, b, off, len);
pos += len; pos += len;
} }
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
ensureOpened();
accessor.flush();
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (accessor != null) {
accessor.close();
}
accessor = null; accessor = null;
} }
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
ensureOpened(); ensurePos();
byte[] buffer = { (byte) b }; byte[] buffer = { (byte) b };
accessor.write(pos, buffer, 0, 1); accessor.write(pos, buffer, 0, 1);
pos++; pos++;
@ -101,4 +101,11 @@ public class TFileOutputStream extends OutputStream {
throw new IOException("This stream is already closed"); throw new IOException("This stream is already closed");
} }
} }
private void ensurePos() throws IOException {
ensureOpened();
if (pos < 0) {
pos = accessor.size();
}
}
} }

View File

@ -30,6 +30,7 @@ import org.teavm.classlib.java.lang.TNullPointerException;
public class TRandomAccessFile implements DataInput, DataOutput, Closeable { public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
private boolean readOnly; private boolean readOnly;
private boolean autoFlush;
private VirtualFileAccessor accessor; private VirtualFileAccessor accessor;
private int pos; private int pos;
private byte[] buff; private byte[] buff;
@ -44,8 +45,10 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
readOnly = true; readOnly = true;
break; break;
case "rw": case "rw":
break;
case "rwd": case "rwd":
case "rws": case "rws":
autoFlush = true;
break; break;
default: default:
throw new IllegalArgumentException("Invalid mode: " + mode); throw new IllegalArgumentException("Invalid mode: " + mode);
@ -56,10 +59,11 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
accessor = virtualFile.createAccessor(); accessor = virtualFile.createAccessor(true, !readOnly, false);
if (accessor == null) { if (accessor == null) {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
buff = new byte[16];
} }
@Override @Override
@ -281,20 +285,17 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
if (readOnly) {
throw new IOException("This instance is read-only");
}
ensureOpened(); ensureOpened();
byte[] buffer = { (byte) b }; byte[] buffer = { (byte) b };
accessor.write(pos, buffer, 0, 1); accessor.write(pos, buffer, 0, 1);
if (autoFlush) {
accessor.flush();
}
pos++; pos++;
} }
@Override @Override
public void write(byte[] b) throws IOException { public void write(byte[] b) throws IOException {
if (readOnly) {
throw new IOException("This instance is read-only");
}
write(b, 0, b.length); write(b, 0, b.length);
} }
@ -306,6 +307,9 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
} }
ensureOpened(); ensureOpened();
accessor.write(pos, b, off, len); accessor.write(pos, b, off, len);
if (autoFlush) {
accessor.flush();
}
pos += len; pos += len;
} }
@ -456,7 +460,7 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
return new String(out, 0, s); return new String(out, 0, s);
} }
static int writeShortToBuffer(int val, byte[] buffer, int offset) throws IOException { static int writeShortToBuffer(int val, byte[] buffer, int offset) {
buffer[offset++] = (byte) (val >> 8); buffer[offset++] = (byte) (val >> 8);
buffer[offset++] = (byte) val; buffer[offset++] = (byte) val;
return offset; return offset;
@ -478,7 +482,7 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
return utfCount; return utfCount;
} }
static int writeUTFBytesToBuffer(String str, byte[] buffer, int offset) throws IOException { static int writeUTFBytesToBuffer(String str, byte[] buffer, int offset) {
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

@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
import org.teavm.classlib.impl.unicode.UnicodeHelper; import org.teavm.classlib.impl.unicode.UnicodeHelper;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.interop.c.Include; import org.teavm.interop.c.Include;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
import org.teavm.platform.metadata.StringResource; import org.teavm.platform.metadata.StringResource;
@ -244,6 +245,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
@Import(module = "teavm", name = "towlower") @Import(module = "teavm", name = "towlower")
@Include("wctype.h") @Include("wctype.h")
@Unmanaged
private static native int toLowerCaseSystem(int codePoint); private static native int toLowerCaseSystem(int codePoint);
public static char toUpperCase(char ch) { public static char toUpperCase(char ch) {
@ -261,6 +263,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
@Import(module = "teavm", name = "towupper") @Import(module = "teavm", name = "towupper")
@Include("wctype.h") @Include("wctype.h")
@Unmanaged
private static native int toUpperCaseSystem(int codePoint); private static native int toUpperCaseSystem(int codePoint);
public static int digit(char ch, int radix) { public static int digit(char ch, int radix) {

View File

@ -19,6 +19,7 @@ import java.io.IOException;
import org.teavm.classlib.java.io.TOutputStream; import org.teavm.classlib.java.io.TOutputStream;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.interop.c.Include; import org.teavm.interop.c.Include;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
@ -38,5 +39,6 @@ class TConsoleOutputStreamStderr extends TOutputStream {
@Include("wchar.h") @Include("wchar.h")
@Import(name = "putwchar", module = "teavm") @Import(name = "putwchar", module = "teavm")
@Unmanaged
static native void writeImpl(int b); static native void writeImpl(int b);
} }

View File

@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
@NoSideEffects @NoSideEffects
@ -233,21 +234,25 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
@JSBody(params = "v", script = "return isNaN(v);") @JSBody(params = "v", script = "return isNaN(v);")
@Import(module = "teavm", name = "isnan") @Import(module = "teavm", name = "isnan")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native boolean isNaN(double v); public static native boolean isNaN(double v);
@JSBody(script = "return NaN;") @JSBody(script = "return NaN;")
@Import(module = "teavm", name = "teavm_getNaN") @Import(module = "teavm", name = "teavm_getNaN")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native double getNaN(); private static native double getNaN();
@JSBody(params = "v", script = "return !isFinite(v);") @JSBody(params = "v", script = "return !isFinite(v);")
@Import(module = "teavm", name = "isinf") @Import(module = "teavm", name = "isinf")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native boolean isInfinite(double v); public static native boolean isInfinite(double v);
@JSBody(params = "v", script = "return isFinite(v);") @JSBody(params = "v", script = "return isFinite(v);")
@Import(module = "teavm", name = "isfinite") @Import(module = "teavm", name = "isfinite")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native boolean isFinite(double v); public static native boolean isFinite(double v);
public static long doubleToRawLongBits(double value) { public static long doubleToRawLongBits(double value) {
@ -257,11 +262,13 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
@InjectedBy(DoubleGenerator.class) @InjectedBy(DoubleGenerator.class)
@Import(name = "teavm_reinterpretDoubleToLong") @Import(name = "teavm_reinterpretDoubleToLong")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native long doubleToLongBits(double value); public static native long doubleToLongBits(double value);
@InjectedBy(DoubleGenerator.class) @InjectedBy(DoubleGenerator.class)
@Import(name = "teavm_reinterpretLongToDouble") @Import(name = "teavm_reinterpretLongToDouble")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native double longBitsToDouble(long bits); public static native double longBitsToDouble(long bits);
public static String toHexString(double d) { public static String toHexString(double d) {

View File

@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
public class TFloat extends TNumber implements TComparable<TFloat> { public class TFloat extends TNumber implements TComparable<TFloat> {
@ -97,21 +98,25 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
@JSBody(params = "v", script = "return isNaN(v);") @JSBody(params = "v", script = "return isNaN(v);")
@Import(module = "teavm", name = "isnan") @Import(module = "teavm", name = "isnan")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native boolean isNaN(float v); public static native boolean isNaN(float v);
@JSBody(params = "v", script = "return !isFinite(v);") @JSBody(params = "v", script = "return !isFinite(v);")
@Import(module = "teavm", name = "isinf") @Import(module = "teavm", name = "isinf")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native boolean isInfinite(float v); public static native boolean isInfinite(float v);
@JSBody(params = "v", script = "return isFinite(v);") @JSBody(params = "v", script = "return isFinite(v);")
@Import(module = "teavm", name = "isfinite") @Import(module = "teavm", name = "isfinite")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native boolean isFinite(float v); public static native boolean isFinite(float v);
@JSBody(script = "return NaN;") @JSBody(script = "return NaN;")
@Import(module = "teavm", name = "teavm_getNaN") @Import(module = "teavm", name = "teavm_getNaN")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native float getNaN(); private static native float getNaN();
public static float parseFloat(TString string) throws TNumberFormatException { public static float parseFloat(TString string) throws TNumberFormatException {
@ -261,11 +266,13 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
@JSBody(params = "value", script = "return $rt_floatToIntBits(value);") @JSBody(params = "value", script = "return $rt_floatToIntBits(value);")
@Import(name = "teavm_reinterpretFloatToInt") @Import(name = "teavm_reinterpretFloatToInt")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native int floatToIntBits(float value); public static native int floatToIntBits(float value);
@JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);") @JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);")
@Import(name = "teavm_reinterpretIntToFloat") @Import(name = "teavm_reinterpretIntToFloat")
@NoSideEffects @NoSideEffects
@Unmanaged
public static native float intBitsToFloat(int bits); public static native float intBitsToFloat(int bits);
public static String toHexString(float f) { public static String toHexString(float f) {

View File

@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
import org.teavm.interop.Unmanaged;
@NoSideEffects @NoSideEffects
public final class TMath extends TObject { public final class TMath extends TObject {
@ -29,26 +30,32 @@ public final class TMath extends TObject {
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "sin") @Import(module = "teavmMath", name = "sin")
@Unmanaged
public static native double sin(double a); public static native double sin(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "cos") @Import(module = "teavmMath", name = "cos")
@Unmanaged
public static native double cos(double a); public static native double cos(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "tan") @Import(module = "teavmMath", name = "tan")
@Unmanaged
public static native double tan(double a); public static native double tan(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "asin") @Import(module = "teavmMath", name = "asin")
@Unmanaged
public static native double asin(double a); public static native double asin(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "acos") @Import(module = "teavmMath", name = "acos")
@Unmanaged
public static native double acos(double a); public static native double acos(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "atan") @Import(module = "teavmMath", name = "atan")
@Unmanaged
public static native double atan(double a); public static native double atan(double a);
public static double toRadians(double angdeg) { public static double toRadians(double angdeg) {
@ -61,10 +68,12 @@ public final class TMath extends TObject {
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "exp") @Import(module = "teavmMath", name = "exp")
@Unmanaged
public static native double exp(double a); public static native double exp(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "log") @Import(module = "teavmMath", name = "log")
@Unmanaged
public static native double log(double a); public static native double log(double a);
public static double log10(double a) { public static double log10(double a) {
@ -73,6 +82,7 @@ public final class TMath extends TObject {
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "sqrt") @Import(module = "teavmMath", name = "sqrt")
@Unmanaged
public static native double sqrt(double a); public static native double sqrt(double a);
public static double cbrt(double a) { public static double cbrt(double a) {
@ -86,14 +96,17 @@ public final class TMath extends TObject {
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "ceil") @Import(module = "teavmMath", name = "ceil")
@Unmanaged
public static native double ceil(double a); public static native double ceil(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "floor") @Import(module = "teavmMath", name = "floor")
@Unmanaged
public static native double floor(double a); public static native double floor(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "pow") @Import(module = "teavmMath", name = "pow")
@Unmanaged
public static native double pow(double x, double y); public static native double pow(double x, double y);
public static double rint(double a) { public static double rint(double a) {
@ -102,6 +115,7 @@ public final class TMath extends TObject {
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "atan2") @Import(module = "teavmMath", name = "atan2")
@Unmanaged
public static native double atan2(double y, double x); public static native double atan2(double y, double x);
public static int round(float a) { public static int round(float a) {
@ -114,6 +128,7 @@ public final class TMath extends TObject {
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "teavmMath", name = "random") @Import(module = "teavmMath", name = "random")
@Unmanaged
public static native double random(); public static native double random();
public static int min(int a, int b) { public static int min(int a, int b) {

View File

@ -19,6 +19,8 @@ import java.util.Enumeration;
import java.util.Properties; import java.util.Properties;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.fs.c.CFileSystem;
import org.teavm.classlib.impl.c.Memory;
import org.teavm.classlib.java.io.TConsole; import org.teavm.classlib.java.io.TConsole;
import org.teavm.classlib.java.io.TInputStream; import org.teavm.classlib.java.io.TInputStream;
import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.io.TPrintStream;
@ -154,11 +156,28 @@ public final class TSystem extends TObject {
defaults.put("line.separator", lineSeparator()); defaults.put("line.separator", lineSeparator());
defaults.put("java.io.tmpdir", "/tmp"); defaults.put("java.io.tmpdir", "/tmp");
defaults.put("java.vm.version", "1.8"); defaults.put("java.vm.version", "1.8");
defaults.put("user.home", "/"); defaults.put("user.home", getHomeDir());
properties = new Properties(defaults); properties = new Properties(defaults);
} }
} }
private static String getHomeDir() {
if (!PlatformDetector.isC()) {
return "/";
}
Address resultPtr = Memory.malloc(Address.sizeOf());
int length = CFileSystem.homeDirectory(resultPtr);
Address result = resultPtr.getAddress();
Memory.free(resultPtr);
char[] chars = new char[length];
Memory.memcpy(Address.ofData(chars), result, chars.length * 2);
Memory.free(result);
return new String(chars);
}
public static String getProperty(@SuppressWarnings("unused") String key) { public static String getProperty(@SuppressWarnings("unused") String key) {
initPropertiesIfNeeded(); initPropertiesIfNeeded();
return properties.getProperty(key); return properties.getProperty(key);

View File

@ -21,6 +21,7 @@ import org.teavm.classlib.java.lang.TComparable;
import org.teavm.classlib.java.lang.TSystem; import org.teavm.classlib.java.lang.TSystem;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.core.JSDate; import org.teavm.jso.core.JSDate;
public class TDate implements TComparable<TDate> { public class TDate implements TComparable<TDate> {
@ -34,6 +35,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_init") @Import(name = "teavm_date_init")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native void initLowLevel(); private static native void initLowLevel();
public TDate() { public TDate() {
@ -66,6 +68,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_create") @Import(name = "teavm_date_create")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native long initDateLowLevel(int year, int month, int date, int hrs, int min, int sec); private static native long initDateLowLevel(int year, int month, int date, int hrs, int min, int sec);
public TDate(String s) { public TDate(String s) {
@ -88,6 +91,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_createUtc") @Import(name = "teavm_date_createUtc")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native long initUtcDateLowLevel(int year, int month, int date, int hrs, int min, int sec); private static native long initUtcDateLowLevel(int year, int month, int date, int hrs, int min, int sec);
@Deprecated @Deprecated
@ -105,6 +109,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_parse") @Import(name = "teavm_date_parse")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native long parseLowLevel(String s); private static native long parseLowLevel(String s);
@Deprecated @Deprecated
@ -117,6 +122,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_getYear") @Import(name = "teavm_date_getYear")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int getYearLowLevel(long date); private static native int getYearLowLevel(long date);
@Deprecated @Deprecated
@ -132,6 +138,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_setYear") @Import(name = "teavm_date_setYear")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native long setYearLowLevel(long date, int year); private static native long setYearLowLevel(long date, int year);
@Deprecated @Deprecated
@ -144,6 +151,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_getMonth") @Import(name = "teavm_date_getMonth")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int getMonthLowLevel(long date); private static native int getMonthLowLevel(long date);
@Deprecated @Deprecated
@ -159,6 +167,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_setMonth") @Import(name = "teavm_date_setMonth")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native long setMonthLowLevel(long date, int month); private static native long setMonthLowLevel(long date, int month);
@Deprecated @Deprecated
@ -171,6 +180,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_getDate") @Import(name = "teavm_date_getDate")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int getDateLowLevel(long date); private static native int getDateLowLevel(long date);
@Deprecated @Deprecated
@ -186,6 +196,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_setDate") @Import(name = "teavm_date_setDate")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int setDateLowLevel(long target, int date); private static native int setDateLowLevel(long target, int date);
@Deprecated @Deprecated
@ -197,6 +208,7 @@ public class TDate implements TComparable<TDate> {
} }
@Import(name = "teavm_date_getDay") @Import(name = "teavm_date_getDay")
@Unmanaged
public static native int getDayLowLevel(long date); public static native int getDayLowLevel(long date);
@Deprecated @Deprecated
@ -224,6 +236,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_setHours") @Import(name = "teavm_date_setHours")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int setHoursLowLevel(long date, int hours); private static native int setHoursLowLevel(long date, int hours);
@Deprecated @Deprecated
@ -236,6 +249,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_getMinutes") @Import(name = "teavm_date_getMinutes")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int getMinutesLowLevel(long date); private static native int getMinutesLowLevel(long date);
@Deprecated @Deprecated
@ -251,6 +265,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_setMinutes") @Import(name = "teavm_date_setMinutes")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int setMinutesLowLevel(long date, int minutes); private static native int setMinutesLowLevel(long date, int minutes);
@Deprecated @Deprecated
@ -263,6 +278,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_getSeconds") @Import(name = "teavm_date_getSeconds")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int getSecondsLowLevel(long date); private static native int getSecondsLowLevel(long date);
@Deprecated @Deprecated
@ -278,6 +294,7 @@ public class TDate implements TComparable<TDate> {
@Import(name = "teavm_date_setSeconds") @Import(name = "teavm_date_setSeconds")
@NoSideEffects @NoSideEffects
@Unmanaged
private static native int setSecondsLowLevel(long date, int seconds); private static native int setSecondsLowLevel(long date, int seconds);
public long getTime() { public long getTime() {

View File

@ -20,6 +20,7 @@ import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.TMath; import org.teavm.classlib.java.lang.TMath;
import org.teavm.classlib.java.lang.TObject; import org.teavm.classlib.java.lang.TObject;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
public class TRandom extends TObject implements TSerializable { public class TRandom extends TObject implements TSerializable {
@ -82,6 +83,7 @@ public class TRandom extends TObject implements TSerializable {
} }
@Import(name = "teavm_rand") @Import(name = "teavm_rand")
@Unmanaged
private static native double crand(); private static native double crand();
/** /**
@ -119,5 +121,6 @@ public class TRandom extends TObject implements TSerializable {
@JSBody(script = "return Math.random();") @JSBody(script = "return Math.random();")
@Import(module = "teavmMath", name = "random") @Import(module = "teavmMath", name = "random")
@Unmanaged
private static native double random(); private static native double random();
} }

View File

@ -379,6 +379,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
copyResource("stringhash.c", buildTarget); copyResource("stringhash.c", buildTarget);
copyResource("references.c", buildTarget); copyResource("references.c", buildTarget);
copyResource("date.c", buildTarget); copyResource("date.c", buildTarget);
copyResource("file.c", buildTarget);
generateCallSites(buildTarget, context, classes.getClassNames()); generateCallSites(buildTarget, context, classes.getClassNames());
generateStrings(buildTarget, context); generateStrings(buildTarget, context);
@ -681,6 +682,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
files.add("callsites.c"); files.add("callsites.c");
files.add("references.c"); files.add("references.c");
files.add("date.c"); files.add("date.c");
files.add("file.c");
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
files.add(ClassGenerator.fileName(className) + ".c"); files.add(ClassGenerator.fileName(className) + ".c");

View File

@ -101,7 +101,7 @@ public class AddressIntrinsic implements Intrinsic {
context.writer().print(")"); context.writer().print(")");
break; break;
case "getChar": case "getChar":
context.writer().print("((int32_t) *(uchar16_t*) "); context.writer().print("((int32_t) *(char16_t*) ");
context.emit(invocation.getArguments().get(0)); context.emit(invocation.getArguments().get(0));
context.writer().print(")"); context.writer().print(")");
break; break;
@ -136,9 +136,9 @@ public class AddressIntrinsic implements Intrinsic {
context.writer().print(")"); context.writer().print(")");
break; break;
case "putChar": case "putChar":
context.writer().print("(*(uchar16_t*) "); context.writer().print("(*(char16_t*) ");
context.emit(invocation.getArguments().get(0)); context.emit(invocation.getArguments().get(0));
context.writer().print(" = (uchar16_t) "); context.writer().print(" = (char16_t) ");
context.emit(invocation.getArguments().get(1)); context.emit(invocation.getArguments().get(1));
context.writer().print(")"); context.writer().print(")");
break; break;
@ -225,6 +225,7 @@ public class AddressIntrinsic implements Intrinsic {
case BYTE: case BYTE:
return 1; return 1;
case SHORT: case SHORT:
case CHARACTER:
return 2; return 2;
case INTEGER: case INTEGER:
case FLOAT: case FLOAT:

View File

@ -43,6 +43,23 @@ inline static int64_t teavm_date_timestamp(struct tm *t) {
return (int64_t) (1000 * difftime(result, teavm_epochStart)); return (int64_t) (1000 * difftime(result, teavm_epochStart));
} }
int64_t teavm_date_timeToTimestamp(time_t t) {
return (int64_t) (1000 * difftime(t, teavm_epochStart));
}
time_t teavm_date_timestampToTime(int64_t timestamp) {
struct tm t = {
.tm_year = 70,
.tm_mon = 0,
.tm_mday = 1,
.tm_hour = 0,
.tm_min = 0,
.tm_sec = timestamp / 1000,
.tm_isdst = -1
};
return timegm(&t) + timestamp % 1000;
}
inline static struct tm* teavm_date_decompose(int64_t timestamp, struct tm *t) { inline static struct tm* teavm_date_decompose(int64_t timestamp, struct tm *t) {
*t = teavm_epochStartTm; *t = teavm_epochStartTm;
t->tm_sec += timestamp / 1000; t->tm_sec += timestamp / 1000;

View File

@ -0,0 +1,309 @@
#include "runtime.h"
#include <stdlib.h>
#include <errno.h>
#ifdef __GNUC__
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <dirent.h>
#include <utime.h>
#include <pwd.h>
int32_t teavm_file_homeDirectory(char16_t** result) {
struct passwd *pw = getpwuid(getuid());
int32_t length;
*result = teavm_mbToChar16(pw->pw_dir, &length);
return length;
}
int32_t teavm_file_workDirectory(char16_t** result) {
long pathMax;
size_t size;
char *buf;
int32_t length;
pathMax = pathconf(".", _PC_PATH_MAX);
if (pathMax == -1) {
size = 1024;
} else if (pathMax > 10240) {
size = 10240;
} else {
size = pathMax;
}
buf = malloc(size);
while (1) {
if (getcwd(buf, size) != buf) {
if (errno != ERANGE) {
*result = teavm_mbToChar16(strerror(errno), &length);
return -1 - length;
}
} else {
*result = teavm_mbToChar16(buf, &length);
free(buf);
return length;
}
size *= 2;
char* newBuf = realloc(buf, size);
if (newBuf == NULL) {
*result = teavm_mbToChar16("Out of memory", &length);
free(buf);
return -1 - length;
}
buf = newBuf;
}
}
int32_t teavm_file_isFile(char16_t* name, int32_t nameSize) {
struct stat s;
char* mbName = teavm_char16ToMb(name, nameSize);
int statResult = stat(mbName, &s);
free(mbName);
if (statResult != 0) {
return 0;
}
return !S_ISDIR(s.st_mode);
}
int32_t teavm_file_isDir(char16_t* name, int32_t nameSize) {
struct stat s;
char* mbName = teavm_char16ToMb(name, nameSize);
int statResult = stat(mbName, &s);
free(mbName);
if (statResult != 0) {
return 0;
}
return S_ISDIR(s.st_mode);
}
int32_t teavm_file_canRead(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
int result = access(mbName, R_OK);
free(mbName);
return result == 0;
}
int32_t teavm_file_canWrite(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
int result = access(mbName, W_OK);
free(mbName);
return result == 0;
}
int32_t teavm_file_setReadonly(char16_t* name, int32_t nameSize, int32_t readonly) {
struct stat s;
char* mbName = teavm_char16ToMb(name, nameSize);
int statResult = stat(mbName, &s);
if (statResult != 0) {
free(mbName);
return 0;
}
mode_t mode = s.st_mode;
if (readonly) {
mode &= ~S_IWUSR;
} else {
mode |= S_IWUSR;
}
int result = chmod(mbName, mode);
free(mbName);
return result == 0;
}
TeaVM_StringList* teavm_file_listFiles(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
DIR* dir = opendir(mbName);
free(mbName);
if (dir == NULL) {
return NULL;
}
TeaVM_StringList *strings = teavm_appendString(NULL, NULL, 0);
while (1) {
struct dirent* entry = readdir(dir);
if (entry == NULL) {
break;
}
int32_t len;
char16_t* name = teavm_mbToChar16(entry->d_name, &len);
if (name == NULL) {
teavm_disposeStringList(strings);
return NULL;
}
strings = teavm_appendString(strings, name, len);
}
closedir(dir);
return strings;
}
int32_t teavm_file_createDirectory(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
int result = mkdir(mbName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
free(mbName);
return result == 0;
}
int32_t teavm_file_createFile(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
int result = open(mbName, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
free(mbName);
if (result >= 0) {
close(result);
return 0;
} else if (errno == EEXIST) {
return 1;
} else {
return 2;
}
}
int32_t teavm_file_delete(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
int result = remove(mbName);
free(mbName);
return result == 0;
}
int32_t teavm_file_rename(char16_t* name, int32_t nameSize, char16_t* newName, int32_t newNameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
char* newMbName = teavm_char16ToMb(newName, newNameSize);
int result = rename(mbName, newMbName);
free(mbName);
free(newMbName);
return result == 0;
}
int64_t teavm_file_lastModified(char16_t* name, int32_t nameSize) {
struct stat s;
char* mbName = teavm_char16ToMb(name, nameSize);
int statResult = stat(mbName, &s);
free(mbName);
if (statResult != 0) {
return 0;
}
return teavm_date_timeToTimestamp(s.st_mtime);
}
int32_t teavm_file_setLastModified(char16_t* name, int32_t nameSize, int64_t lastModified) {
struct stat s;
char* mbName = teavm_char16ToMb(name, nameSize);
int statResult = stat(mbName, &s);
if (statResult != 0) {
free(mbName);
return 0;
}
struct utimbuf newTime;
newTime.actime = s.st_atime;
newTime.modtime = teavm_date_timestampToTime(lastModified);
return utime(mbName, &newTime) == 0;
}
int32_t teavm_file_length(char16_t* name, int32_t nameSize) {
char* mbName = teavm_char16ToMb(name, nameSize);
FILE* file = fopen(mbName, "r");
free(mbName);
if (file == NULL) {
return 0;
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return 0;
}
long result = ftell(file);
fclose(file);
if (result < 0) {
return 0;
}
return (int32_t) result;
}
int64_t teavm_file_open(char16_t* name, int32_t nameSize, int32_t mode) {
char* mbName = teavm_char16ToMb(name, nameSize);
char* modeString;
switch (mode) {
case 1:
modeString = "r";
break;
case 2:
modeString = "w";
break;
case 3:
modeString = "w+";
break;
case 6:
modeString = "a";
break;
case 7:
modeString = "a+";
break;
default:
modeString = "";
break;
}
FILE* file = fopen(mbName, modeString);
if (file == NULL) {
return 0;
}
return (int64_t) (intptr_t) file;
}
int32_t teavm_file_close(int64_t file) {
FILE* handle = (FILE*) file;
return fclose(handle) == 0;
}
int32_t teavm_file_flush(int64_t file) {
FILE* handle = (FILE*) file;
return fflush(handle) == 0;
}
int32_t teavm_file_seek(int64_t file, int32_t where, int32_t offset) {
FILE* handle = (FILE*) file;
int whence;
switch (where) {
case 0:
whence = SEEK_SET;
break;
case 1:
whence = SEEK_CUR;
break;
case 2:
whence = SEEK_END;
break;
}
return fseek(handle, offset, whence) == 0;
}
int32_t teavm_file_tell(int64_t file) {
FILE* handle = (FILE*) file;
return (int32_t) ftell(handle);
}
int32_t teavm_file_read(int64_t file, int8_t* data, int32_t offset, int32_t size) {
FILE* handle = (FILE*) file;
return (int32_t) fread(data + offset, 1, size, handle);
}
int32_t teavm_file_write(int64_t file, int8_t* data, int32_t offset, int32_t size) {
FILE* handle = (FILE*) file;
return (int32_t) fwrite(data + offset, 1, size, handle);
}
#endif

View File

@ -14,6 +14,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <signal.h> #include <signal.h>
#include <locale.h>
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
@ -64,6 +65,8 @@ void teavm_beforeInit() {
srand(time(NULL)); srand(time(NULL));
#ifdef __GNUC__ #ifdef __GNUC__
setlocale (LC_ALL, "");
struct sigaction sigact; struct sigaction sigact;
sigact.sa_flags = 0; sigact.sa_flags = 0;
sigact.sa_handler = NULL; sigact.sa_handler = NULL;
@ -377,6 +380,42 @@ TeaVM_String* teavm_cToString(char* cstring) {
return teavm_createString(charArray); return teavm_createString(charArray);
} }
char16_t* teavm_mbToChar16(char* cstring, int32_t* length) {
size_t clen = strlen(cstring);
int32_t size = teavm_c16Size(cstring, clen);
char16_t* javaChars = malloc(sizeof(char16_t) * (size + 2));
mbstate_t state = {0};
for (int32_t i = 0; i < size; ++i) {
int32_t result = mbrtoc16(javaChars + i, cstring, clen, &state);
if (result == -1) {
break;
} else if ((int) result >= 0) {
clen -= result;
cstring += result;
}
}
*length = size;
return javaChars;
}
char* teavm_char16ToMb(char16_t* javaChars, int32_t length) {
size_t sz = teavm_mbSize(javaChars, length);
char* cchars = malloc(sz + 1);
int32_t j = 0;
char* dst = cchars;
mbstate_t state = {0};
for (int32_t i = 0; i < length; ++i) {
size_t result = c16rtomb(dst, javaChars[i], &state);
if (result == -1) {
break;
}
dst += result;
}
*dst = '\0';
return cchars;
}
TeaVM_Array* teavm_parseArguments(int argc, char** argv) { TeaVM_Array* teavm_parseArguments(int argc, char** argv) {
TeaVM_Array* array = teavm_allocateStringArray(argc - 1); TeaVM_Array* array = teavm_allocateStringArray(argc - 1);
TeaVM_String** arrayData = TEAVM_ARRAY_DATA(array, TeaVM_String*); TeaVM_String** arrayData = TEAVM_ARRAY_DATA(array, TeaVM_String*);
@ -438,3 +477,25 @@ void teavm_afterInitClasses() {
builder = next; builder = next;
} }
} }
void teavm_disposeStringList(TeaVM_StringList* list) {
while (list != NULL) {
TeaVM_StringList* next = list->next;
if (list->data != NULL) {
free(list->data);
}
free(list);
list = next;
}
}
TeaVM_StringList* teavm_appendString(TeaVM_StringList* list, char16_t* data, int32_t length) {
TeaVM_StringList* entry = malloc(sizeof(TeaVM_StringList));
if (entry == NULL) {
teavm_disposeStringList(list);
return NULL;
}
entry->data = data;
entry->length = length;
entry->next = list;
return entry;
}

View File

@ -207,6 +207,8 @@ extern int32_t teavm_timeZoneOffset();
extern char* teavm_stringToC(void*); extern char* teavm_stringToC(void*);
extern TeaVM_String* teavm_cToString(char*); extern TeaVM_String* teavm_cToString(char*);
extern char16_t* teavm_mbToChar16(char*, int32_t*);
extern char* teavm_char16ToMb(char16_t*, int32_t);
static inline void teavm_free(void* s) { static inline void teavm_free(void* s) {
if (s != NULL) { if (s != NULL) {
free(s); free(s);
@ -238,12 +240,12 @@ extern void teavm_waitFor(int64_t timeout);
extern void teavm_interrupt(); extern void teavm_interrupt();
extern void teavm_outOfMemory(); extern void teavm_outOfMemory();
extern void teavm_printString(char* s); extern void teavm_printString(char*);
extern void teavm_printInt(int32_t i); extern void teavm_printInt(int32_t);
extern TeaVM_Array* teavm_parseArguments(int argc, char** argv); extern TeaVM_Array* teavm_parseArguments(int, char**);
extern void teavm_registerStaticGcRoots(void***, int count); extern void teavm_registerStaticGcRoots(void***, int);
extern TeaVM_String* teavm_registerString(TeaVM_String*); extern TeaVM_String* teavm_registerString(TeaVM_String*);
@ -279,9 +281,11 @@ extern TeaVM_Reference* teavm_reference_poll(TeaVM_ReferenceQueue*);
extern void teavm_reference_init(TeaVM_Reference*, TeaVM_Object*, TeaVM_ReferenceQueue*); extern void teavm_reference_init(TeaVM_Reference*, TeaVM_Object*, TeaVM_ReferenceQueue*);
extern void teavm_date_init(); extern void teavm_date_init();
extern int64_t teavm_date_timeToTimestamp(time_t);
extern time_t teavm_date_timestampToTime(int64_t);
extern int64_t teavm_date_create(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t); extern int64_t teavm_date_create(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int64_t teavm_date_createUtc(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t); extern int64_t teavm_date_createUtc(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int64_t teavm_date_parse(char* s); extern int64_t teavm_date_parse(char*);
extern int32_t teavm_date_getYear(int64_t); extern int32_t teavm_date_getYear(int64_t);
extern int64_t teavm_date_setYear(int64_t,int32_t); extern int64_t teavm_date_setYear(int64_t,int32_t);
extern int32_t teavm_date_getMonth(int64_t); extern int32_t teavm_date_getMonth(int64_t);
@ -296,3 +300,43 @@ extern int64_t teavm_date_setMinutes(int64_t,int32_t);
extern int32_t teavm_date_getSeconds(int64_t); extern int32_t teavm_date_getSeconds(int64_t);
extern int64_t teavm_date_setSeconds(int64_t,int32_t); extern int64_t teavm_date_setSeconds(int64_t,int32_t);
extern char* teavm_date_format(int64_t); extern char* teavm_date_format(int64_t);
#define TEAVM_SURROGATE_BIT_MASK 0xFC00
#define TEAVM_SURROGATE_INV_BIT_MASK 0x03FF
#define TEAVM_HIGH_SURROGATE_BITS 0xD800
#define TEAVM_LOW_SURROGATE_BITS 0xDC00
#define TEAVM_MIN_SUPPLEMENTARY_CODE_POINT 0x010000
//extern int32_t teavm_utf8_encode(char16_t*, int32_t, char*);
//extern int32_t teavm_utf8_decode(char*, int32_t, char16_t*);
typedef struct TeaVM_StringList {
char16_t* data;
int32_t length;
struct TeaVM_StringList* next;
} TeaVM_StringList;
extern void teavm_disposeStringList(TeaVM_StringList*);
extern TeaVM_StringList* teavm_appendString(TeaVM_StringList*, char16_t*, int32_t);
extern int32_t teavm_file_homeDirectory(char16_t**);
extern int32_t teavm_file_workDirectory(char16_t**);
extern int32_t teavm_file_isFile(char16_t*, int32_t);
extern int32_t teavm_file_isDir(char16_t*, int32_t);
extern int32_t teavm_file_canRead(char16_t*, int32_t);
extern int32_t teavm_file_canWrite(char16_t*, int32_t);
extern TeaVM_StringList* teavm_file_listFiles(char16_t*, int32_t);
extern int32_t teavm_file_createDirectory(char16_t*, int32_t);
extern int32_t teavm_file_createFile(char16_t*, int32_t);
extern int32_t teavm_file_delete(char16_t*, int32_t);
extern int32_t teavm_file_rename(char16_t*, int32_t, char16_t*, int32_t);
extern int64_t teavm_file_lastModified(char16_t*, int32_t);
extern int32_t teavm_file_setLastModified(char16_t*, int32_t, int64_t);
extern int32_t teavm_file_length(char16_t*, int32_t);
extern int64_t teavm_file_open(char16_t*, int32_t, int32_t);
extern int32_t teavm_file_close(int64_t);
extern int32_t teavm_file_flush(int64_t);
extern int32_t teavm_file_seek(int64_t, int32_t, int32_t);
extern int32_t teavm_file_tell(int64_t);
extern int32_t teavm_file_read(int64_t, int8_t*, int32_t, int32_t);
extern int32_t teavm_file_write(int64_t, int8_t*, int32_t, int32_t);

View File

@ -0,0 +1,119 @@
#include "runtime.h"
#include <stdlib.h>
#define teavm_utf8_encodeSingle16(ch, char* target) \
*target++ = (char) (0xE0 | (ch >> 12)); \
*target++ = (char) (0x80 | ((ch >> 6) & 0x3F)); \
*target++ = (char) (0x80 | (ch & 0x3F));
int32_t teavm_utf8_encode(char16_t* source, int32_t sourceSize, char* target) {
char* initialTarget = target;
while (sourceSize-- > 0) {
char16_t ch = *source++;
if (ch < 0x80) {
**target++ = (char) ch;
} else if (ch < 0x800) {
*target++ = (char) (0xC0 | (ch >> 6));
*target++ = (char) (0x80 | (ch & 0x3F));
} else {
if (ch & TEAVM_SURROGATE_BIT_MASK == TEAVM_HIGH_SURROGATE_BITS) {
if (sourceSize-- == 0) {
teavm_utf8_encodeSingle16(ch, target);
break;
}
char16_t nextCh = *source;
if (ch & TEAVM_SURROGATE_BIT_MASK != TEAVM_LOW_SURROGATE_BITS) {
teavm_utf8_encodeSingle16(ch, target);
continue;
}
source++;
sourceSize--;
int32_t codePoint = (((ch & TEAVM_SURROGATE_BIT_INV_MASK) << 10) | (nextCh & SURROGATE_BIT_INV_MASK))
+ TEAVM_MIN_SUPPLEMENTARY_CODE_POINT;
*target++ = (char) (0xF0 | (codePoint >> 18));
*target++ = (char) (0x80 | ((codePoint >> 12) & 0x3F));
*target++ = (char) (0x80 | ((codePoint >> 6) & 0x3F));
*target++ = (char) (0x80 | (codePoint & 0x3F));
} else {
teavm_utf8_encodeSingle16(ch, target);
}
}
}
return (int32_t) (target - initialTarget);
}
int32_t teavm_utf8_decode(char* source, int32_t sourceSize, char16_t* target) {
char16_t* initialTarget = target;
while (sourceSize-- > 0) {
char b = *source++;
if ((b & 0x80) == 0) {
*target++ = (char16_t) b;
} else if ((b & 0xE0) == 0xC0) {
if (sourceSize-- == 0) {
*target++ = (char16_t) b;
break;
}
char b2 = *source;
if ((b2 & 0xC0) != 0x80) {
*target++ = (char16_t) b;
continue;
}
source++;
sourceSize--;
*target++ = (char16_t) ((((char16_t) b & 0x1F) << 6) | ((char16_t) b2 & 0x3F);
} else if ((b & 0xF0) == 0xE0) {
if (sourceSize < 2) {
*target++ = (char16_t) b;
continue;
}
char b2 = source[0];
char b3 = source[1];
if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
*target++ = (char16_t) b;
continue;
}
source += 2;
sourceSize -= 2;
char16_t c = (char16_t)
((((char16_t) b & 0x0F) << 12)
| (((char16_t) b2 & 0x3F) << 6)
| ((char16_t) b3 & 0x3F));
*target++ = c;
} else if ((b & 0xF8) == 0xF0) {
if (sourceSize < 3) {
*target++ = (char16_t) b;
continue;
}
char b2 = source[0];
char b3 = source[1];
char b4 = source[2];
if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) {
*target++ = (char16_t) b;
continue;
}
source += 3;
sourceSize -= 3;
int32_t code = (int32_t)
((((int32_t) b & 0x07) << 18)
| (((int32_t) b2 & 0x3f) << 12)
| (((int32_t) b3 & 0x3F) << 6)
| (((int32_t) b4 & 0x3F)));
*target++ = (char16_t) ((TEAVM_HIGH_SURROGATE_BITS | ((code - TEAVM_MIN_SUPPLEMENTARY_CODE_POINT) >> 10)
& SURROGATE_BIT_INV_MASK))
*target++ = (char16_t) (TEAVM_LOW_SURROGATE_BITS | code & TEAVM_SURROGATE_BIT_INV_MASK);
} else {
*target++ = (char16_t) b;
}
}
return (int32_t) (target - initialTarget);
}

View File

@ -189,10 +189,11 @@ public class FileOutputStreamTest {
f = new File(System.getProperty("user.home"), "output.tst"); f = new File(System.getProperty("user.home"), "output.tst");
fos = new FileOutputStream(f.getPath()); fos = new FileOutputStream(f.getPath());
fos.write(fileString.getBytes()); fos.write(fileString.getBytes());
fos.close();
fis = new FileInputStream(f.getPath()); fis = new FileInputStream(f.getPath());
byte[] rbytes = new byte[4000]; byte[] rbytes = new byte[4000];
fis.read(rbytes, 0, fileString.length()); fis.read(rbytes, 0, fileString.length());
assertTrue("Incorrect string returned", new String(rbytes, 0, fileString.length()).equals(fileString)); assertEquals("Incorrect string returned", fileString, new String(rbytes, 0, fileString.length()));
} }
@Test @Test
@ -200,10 +201,11 @@ public class FileOutputStreamTest {
f = new File(System.getProperty("user.home"), "output.tst"); f = new File(System.getProperty("user.home"), "output.tst");
fos = new FileOutputStream(f.getPath()); fos = new FileOutputStream(f.getPath());
fos.write(fileString.getBytes(), 0, fileString.length()); fos.write(fileString.getBytes(), 0, fileString.length());
fos.close();
fis = new FileInputStream(f.getPath()); fis = new FileInputStream(f.getPath());
byte[] rbytes = new byte[4000]; byte[] rbytes = new byte[4000];
fis.read(rbytes, 0, fileString.length()); fis.read(rbytes, 0, fileString.length());
assertTrue("Incorrect bytes written", new String(rbytes, 0, fileString.length()).equals(fileString)); assertEquals("Incorrect bytes written", fileString, new String(rbytes, 0, fileString.length()));
// Regression test for HARMONY-285 // Regression test for HARMONY-285
File file = new File("FileOutputStream.tmp"); File file = new File("FileOutputStream.tmp");
@ -224,6 +226,7 @@ public class FileOutputStreamTest {
f = new File(System.getProperty("user.home"), "output.tst"); f = new File(System.getProperty("user.home"), "output.tst");
fos = new FileOutputStream(f.getPath()); fos = new FileOutputStream(f.getPath());
fos.write('t'); fos.write('t');
fos.close();
fis = new FileInputStream(f.getPath()); fis = new FileInputStream(f.getPath());
assertEquals("Incorrect char written", 't', fis.read()); assertEquals("Incorrect char written", 't', fis.read());
} }

View File

@ -1323,7 +1323,7 @@ public class FileTest {
// it a new directory name.) // it a new directory name.)
if (baseDir.exists()) { if (baseDir.exists()) {
dirNumber++; dirNumber++;
baseDir = new File(base, String.valueOf(dirNumber)); baseDir = new File(base, platformId + String.valueOf(dirNumber));
} else { } else {
dirExists = false; dirExists = false;
} }

View File

@ -56,10 +56,11 @@ class CRunStrategy implements TestRunStrategy {
writeLines(compilerOutput); writeLines(compilerOutput);
List<String> runtimeOutput = new ArrayList<>(); List<String> runtimeOutput = new ArrayList<>();
List<String> stdout = new ArrayList<>();
outputFile.setExecutable(true); outputFile.setExecutable(true);
runProcess(new ProcessBuilder(outputFile.getPath()).start(), runtimeOutput); runProcess(new ProcessBuilder(outputFile.getPath()).start(), runtimeOutput, stdout);
if (!runtimeOutput.isEmpty() && runtimeOutput.get(runtimeOutput.size() - 1).equals("SUCCESS")) { if (!stdout.isEmpty() && stdout.get(stdout.size() - 1).equals("SUCCESS")) {
writeLines(runtimeOutput.subList(0, runtimeOutput.size() - 1)); writeLines(runtimeOutput);
run.getCallback().complete(); run.getCallback().complete();
} else { } else {
run.getCallback().error(new RuntimeException("Test failed:\n" + mergeLines(runtimeOutput))); run.getCallback().error(new RuntimeException("Test failed:\n" + mergeLines(runtimeOutput)));
@ -83,12 +84,13 @@ class CRunStrategy implements TestRunStrategy {
} }
} }
private boolean runCompiler(File inputDir, List<String> output) throws IOException, InterruptedException { private boolean runCompiler(File inputDir, List<String> output)
throws IOException, InterruptedException {
String command = new File(compilerCommand).getAbsolutePath(); String command = new File(compilerCommand).getAbsolutePath();
return runProcess(new ProcessBuilder(command).directory(inputDir).start(), output); return runProcess(new ProcessBuilder(command).directory(inputDir).start(), output, new ArrayList<>());
} }
private boolean runProcess(Process process, List<String> output) throws InterruptedException { private boolean runProcess(Process process, List<String> output, List<String> stdout) throws InterruptedException {
BufferedReader stdin = new BufferedReader(new InputStreamReader(process.getInputStream())); BufferedReader stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
ConcurrentLinkedQueue<String> lines = new ConcurrentLinkedQueue<>(); ConcurrentLinkedQueue<String> lines = new ConcurrentLinkedQueue<>();
@ -109,7 +111,6 @@ class CRunStrategy implements TestRunStrategy {
thread.setDaemon(true); thread.setDaemon(true);
thread.start(); thread.start();
thread = new Thread(() -> {
try { try {
while (true) { while (true) {
String line = stdin.readLine(); String line = stdin.readLine();
@ -117,13 +118,11 @@ class CRunStrategy implements TestRunStrategy {
break; break;
} }
lines.add(line); lines.add(line);
stdout.add(line);
} }
} catch (IOException e) { } catch (IOException e) {
// do nothing // do nothing
} }
});
thread.setDaemon(true);
thread.start();
boolean result = process.waitFor() == 0; boolean result = process.waitFor() == 0;
output.addAll(lines); output.addAll(lines);

View File

@ -101,6 +101,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
private static final String C_ENABLED = "teavm.junit.c"; private static final String C_ENABLED = "teavm.junit.c";
private static final String WASM_ENABLED = "teavm.junit.wasm"; private static final String WASM_ENABLED = "teavm.junit.wasm";
private static final String C_COMPILER = "teavm.junit.c.compiler"; private static final String C_COMPILER = "teavm.junit.c.compiler";
private static final String C_LINE_NUMBERS = "teavm.junit.c.lineNumbers";
private static final String MINIFIED = "teavm.junit.minified"; private static final String MINIFIED = "teavm.junit.minified";
private static final String OPTIMIZED = "teavm.junit.optimized"; private static final String OPTIMIZED = "teavm.junit.optimized";
private static final String FAST_ANALYSIS = "teavm.junit.fastAnalysis"; private static final String FAST_ANALYSIS = "teavm.junit.fastAnalysis";
@ -607,10 +608,16 @@ public class TeaVMTestRunner extends Runner implements Filterable {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}; };
return compileTest(method, configuration, CTarget::new, TestNativeEntryPoint.class.getName(), path, ".c", return compileTest(method, configuration, this::createCTarget, TestNativeEntryPoint.class.getName(), path, ".c",
postBuild, true); postBuild, true);
} }
private CTarget createCTarget() {
CTarget cTarget = new CTarget();
cTarget.setLineNumbersGenerated(Boolean.parseBoolean(System.getProperty(C_LINE_NUMBERS, "false")));
return cTarget;
}
private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration, private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration,
File path) { File path) {
return compileTest(method, configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path, return compileTest(method, configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path,