mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C: implement native file system
This commit is contained in:
parent
6c9393548a
commit
91de1f6ca7
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.classlib.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface VirtualFile {
|
||||
String getName();
|
||||
|
||||
|
@ -22,19 +24,17 @@ public interface VirtualFile {
|
|||
|
||||
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();
|
||||
|
||||
void adopt(VirtualFile targetDir, String fileName);
|
||||
boolean adopt(VirtualFile file, String fileName);
|
||||
|
||||
boolean canRead();
|
||||
|
||||
|
@ -42,9 +42,9 @@ public interface VirtualFile {
|
|||
|
||||
long lastModified();
|
||||
|
||||
void setLastModified(long lastModified);
|
||||
boolean setLastModified(long lastModified);
|
||||
|
||||
void setReadOnly(boolean readOnly);
|
||||
boolean setReadOnly(boolean readOnly);
|
||||
|
||||
int length();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,11 @@ public interface VirtualFileAccessor {
|
|||
|
||||
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 close() throws IOException;
|
||||
|
||||
void flush() throws IOException;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package org.teavm.classlib.fs;
|
||||
|
||||
public interface VirtualFileSystem {
|
||||
VirtualFile getRootFile();
|
||||
|
||||
String getUserDir();
|
||||
|
||||
VirtualFile getFile(String path);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,20 @@
|
|||
*/
|
||||
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 {
|
||||
private static VirtualFileSystem instance = new InMemoryVirtualFileSystem();
|
||||
private static VirtualFileSystem instance;
|
||||
|
||||
static {
|
||||
if (PlatformDetector.isC()) {
|
||||
instance = new CFileSystem();
|
||||
} else {
|
||||
instance = new InMemoryVirtualFileSystem();
|
||||
}
|
||||
}
|
||||
|
||||
private VirtualFileSystemProvider() {
|
||||
}
|
||||
|
|
173
classlib/src/main/java/org/teavm/classlib/fs/c/CFileSystem.java
Normal file
173
classlib/src/main/java/org/teavm/classlib/fs/c/CFileSystem.java
Normal 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);
|
||||
}
|
180
classlib/src/main/java/org/teavm/classlib/fs/c/CVirtualFile.java
Normal file
180
classlib/src/main/java/org/teavm/classlib/fs/c/CVirtualFile.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,9 +13,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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;
|
||||
InMemoryVirtualDirectory parent;
|
||||
long lastModified = System.currentTimeMillis();
|
||||
|
@ -25,44 +28,71 @@ public abstract class AbstractInMemoryVirtualFile implements VirtualFile {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
public boolean delete() {
|
||||
if (parent == null || (isDirectory() && listFiles().length > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parent != null && !parent.canWrite()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parent.children.remove(name);
|
||||
parent.modify();
|
||||
parent = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isDirectory();
|
||||
|
||||
public abstract boolean isFile();
|
||||
|
||||
public abstract String[] listFiles();
|
||||
|
||||
public boolean canRead() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite() {
|
||||
return !readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModified(long lastModified) {
|
||||
public boolean setLastModified(long lastModified) {
|
||||
if (readOnly) {
|
||||
return false;
|
||||
}
|
||||
this.lastModified = lastModified;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
public boolean setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
return true;
|
||||
}
|
||||
|
||||
void modify() {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,13 +13,15 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.Map;
|
||||
import org.teavm.classlib.fs.VirtualFileAccessor;
|
||||
|
||||
public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile {
|
||||
final Map<String, VirtualFile> children = new LinkedHashMap<>();
|
||||
final Map<String, AbstractInMemoryVirtualFile> children = new LinkedHashMap<>();
|
||||
|
||||
InMemoryVirtualDirectory(String name) {
|
||||
super(name);
|
||||
|
@ -36,46 +38,61 @@ public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile {
|
|||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile[] listFiles() {
|
||||
return children.values().toArray(new VirtualFile[0]);
|
||||
public String[] listFiles() {
|
||||
return children.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getChildFile(String fileName) {
|
||||
public AbstractInMemoryVirtualFile getChildFile(String fileName) {
|
||||
return children.get(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFileAccessor createAccessor() {
|
||||
throw new UnsupportedOperationException();
|
||||
public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@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);
|
||||
adoptFile(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile createDirectory(String fileName) {
|
||||
public InMemoryVirtualDirectory createDirectory(String fileName) {
|
||||
if (!canWrite() || getChildFile(fileName) != null) {
|
||||
return null;
|
||||
}
|
||||
InMemoryVirtualDirectory file = new InMemoryVirtualDirectory(fileName);
|
||||
adoptFile(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adopt(VirtualFile file, String fileName) {
|
||||
AbstractInMemoryVirtualFile typedFile = (AbstractInMemoryVirtualFile) file;
|
||||
typedFile.parent.children.remove(typedFile.name);
|
||||
typedFile.parent = this;
|
||||
children.put(fileName, typedFile);
|
||||
typedFile.name = fileName;
|
||||
public boolean adopt(AbstractInMemoryVirtualFile file, String fileName) {
|
||||
if (!canWrite()) {
|
||||
return false;
|
||||
}
|
||||
if (!file.parent.canWrite()) {
|
||||
return false;
|
||||
}
|
||||
file.parent.children.remove(file.name);
|
||||
file.parent = this;
|
||||
children.put(fileName, file);
|
||||
file.name = fileName;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
throw new UnsupportedOperationException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void adoptFile(AbstractInMemoryVirtualFile file) {
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,10 +13,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.fs;
|
||||
package org.teavm.classlib.fs.memory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import org.teavm.classlib.fs.VirtualFileAccessor;
|
||||
|
||||
public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
|
||||
byte[] data = new byte[0];
|
||||
|
@ -37,31 +38,31 @@ public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
|
|||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile[] listFiles() {
|
||||
public String[] listFiles() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractInMemoryVirtualFile getChildFile(String fileName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getChildFile(String fileName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFileAccessor createAccessor() {
|
||||
public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VirtualFileAccessor() {
|
||||
@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));
|
||||
System.arraycopy(data, pos, buffer, offset, limit);
|
||||
return limit;
|
||||
}
|
||||
|
||||
@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);
|
||||
System.arraycopy(buffer, offset, data, pos, limit);
|
||||
size = pos + limit;
|
||||
|
@ -74,27 +75,35 @@ public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void resize(int size) throws IOException {
|
||||
public void resize(int size) {
|
||||
expandData(size);
|
||||
InMemoryVirtualFile.this.size = size;
|
||||
modify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile createDirectory(String fileName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adopt(VirtualFile file, String fileName) {
|
||||
throw new UnsupportedOperationException();
|
||||
public boolean adopt(AbstractInMemoryVirtualFile file, String fileName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,15 +13,18 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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 {
|
||||
private InMemoryVirtualDirectory root = new InMemoryVirtualDirectory("");
|
||||
InMemoryVirtualDirectory root = new InMemoryVirtualDirectory("");
|
||||
private String userDir = "/";
|
||||
|
||||
@Override
|
||||
public VirtualFile getRootFile() {
|
||||
return root;
|
||||
public VirtualFile getFile(String path) {
|
||||
return new VirtualFileImpl(this, path);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
41
classlib/src/main/java/org/teavm/classlib/impl/c/Memory.java
Normal file
41
classlib/src/main/java/org/teavm/classlib/impl/c/Memory.java
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.teavm.backend.javascript.spi.GeneratedBy;
|
|||
import org.teavm.classlib.impl.Base46;
|
||||
import org.teavm.classlib.impl.CharFlow;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.platform.metadata.ResourceMap;
|
||||
|
||||
|
@ -190,6 +191,7 @@ public final class DateTimeZoneProvider {
|
|||
|
||||
@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
|
||||
@Import(module = "teavm", name = "getNativeOffset")
|
||||
@Unmanaged
|
||||
private static native int getNativeOffset(double instant);
|
||||
|
||||
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();
|
||||
|
|
|
@ -258,16 +258,10 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
|
||||
public String[] list() {
|
||||
VirtualFile virtualFile = findVirtualFile();
|
||||
if (virtualFile == null || !virtualFile.isDirectory()) {
|
||||
if (virtualFile == null) {
|
||||
return null;
|
||||
}
|
||||
VirtualFile[] entries = virtualFile.listFiles();
|
||||
String[] names = new String[entries.length];
|
||||
for (int i = 0; i < entries.length; ++i) {
|
||||
names[i] = entries[i].getName();
|
||||
}
|
||||
|
||||
return names;
|
||||
return virtualFile.listFiles();
|
||||
}
|
||||
|
||||
public String[] list(TFilenameFilter filter) {
|
||||
|
@ -289,13 +283,16 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
|
||||
public TFile[] listFiles() {
|
||||
VirtualFile virtualFile = findVirtualFile();
|
||||
if (virtualFile == null || !virtualFile.isDirectory()) {
|
||||
if (virtualFile == null) {
|
||||
return null;
|
||||
}
|
||||
String[] entries = virtualFile.listFiles();
|
||||
if (entries == null) {
|
||||
return null;
|
||||
}
|
||||
VirtualFile[] entries = virtualFile.listFiles();
|
||||
TFile[] files = new TFile[entries.length];
|
||||
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;
|
||||
|
@ -336,7 +333,11 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
}
|
||||
|
||||
public boolean exists() {
|
||||
return findVirtualFile() != null;
|
||||
VirtualFile virtualFile = findVirtualFile();
|
||||
if (virtualFile == null) {
|
||||
return false;
|
||||
}
|
||||
return virtualFile.isDirectory() || virtualFile.isFile();
|
||||
}
|
||||
|
||||
public long lastModified() {
|
||||
|
@ -349,21 +350,24 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
throw new IllegalArgumentException();
|
||||
}
|
||||
VirtualFile file = findVirtualFile();
|
||||
if (file == null || !file.canWrite()) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file.setLastModified(time);
|
||||
return true;
|
||||
return file.setLastModified(time);
|
||||
}
|
||||
|
||||
public boolean setReadOnly() {
|
||||
VirtualFile file = findVirtualFile();
|
||||
if (file == null || !file.canWrite()) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
file.setReadOnly(true);
|
||||
return true;
|
||||
return file.setReadOnly(true);
|
||||
}
|
||||
|
||||
public boolean setWritable(boolean writable) {
|
||||
VirtualFile file = findVirtualFile();
|
||||
return file.setReadOnly(!writable);
|
||||
}
|
||||
|
||||
public long length() {
|
||||
|
@ -376,51 +380,35 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
if (parentVirtualFile == null) {
|
||||
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 false;
|
||||
}
|
||||
|
||||
return parentVirtualFile.createFile(getName()) != null;
|
||||
return parentVirtualFile.createFile(getName());
|
||||
}
|
||||
|
||||
public boolean mkdir() {
|
||||
VirtualFile virtualFile = findParentFile();
|
||||
if (virtualFile == null || !virtualFile.isDirectory() || !virtualFile.canWrite()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return virtualFile.createDirectory(getName()) != null;
|
||||
return virtualFile != null && virtualFile.createDirectory(getName());
|
||||
}
|
||||
|
||||
public boolean mkdirs() {
|
||||
String path = getCanonicalPathImpl();
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
VirtualFile virtualFile = fs().getRootFile();
|
||||
int i = 0;
|
||||
int i = path.indexOf('/');
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
while (i < path.length()) {
|
||||
int next = path.indexOf('/', i);
|
||||
if (next < 0) {
|
||||
next = path.length();
|
||||
}
|
||||
|
||||
String name = path.substring(i, next);
|
||||
VirtualFile child = virtualFile.getChildFile(name);
|
||||
if (child == null) {
|
||||
if (!virtualFile.canWrite()) {
|
||||
VirtualFile parent = fs().getFile(path.substring(0, i));
|
||||
if (!parent.createDirectory(path.substring(i, next))) {
|
||||
VirtualFile child = fs().getFile(path.substring(0, next));
|
||||
if (!child.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
virtualFile = virtualFile.createDirectory(name);
|
||||
} else if (child.isFile()) {
|
||||
return false;
|
||||
} else {
|
||||
virtualFile = child;
|
||||
}
|
||||
|
||||
i = next + 1;
|
||||
|
@ -431,18 +419,7 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
|
||||
public boolean delete() {
|
||||
VirtualFile virtualFile = findVirtualFile();
|
||||
if (virtualFile == null || virtualFile == fs().getRootFile()
|
||||
|| (virtualFile.isDirectory() && virtualFile.listFiles().length > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VirtualFile parentVirtualFile = findParentFile();
|
||||
if (parentVirtualFile != null && !parentVirtualFile.canWrite()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtualFile.delete();
|
||||
return true;
|
||||
return virtualFile.delete();
|
||||
}
|
||||
|
||||
public void deleteOnExit() {
|
||||
|
@ -451,7 +428,7 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
|
||||
public boolean renameTo(TFile dest) {
|
||||
VirtualFile targetDir = dest.findParentFile();
|
||||
if (targetDir == null || !targetDir.isDirectory()) {
|
||||
if (targetDir == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -460,8 +437,7 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
return false;
|
||||
}
|
||||
|
||||
targetDir.adopt(virtualFile, dest.getName());
|
||||
return true;
|
||||
return targetDir.adopt(virtualFile, dest.getName());
|
||||
}
|
||||
|
||||
public URI toURI() {
|
||||
|
@ -589,7 +565,7 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
separatorIndex++;
|
||||
}
|
||||
if (separatorIndex > 0) {
|
||||
name = name.substring(separatorIndex, name.length());
|
||||
name = name.substring(separatorIndex);
|
||||
}
|
||||
|
||||
if (!dirPath.isEmpty() && dirPath.charAt(dirPath.length() - 1) == separatorChar) {
|
||||
|
@ -602,28 +578,7 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
}
|
||||
|
||||
VirtualFile findVirtualFile() {
|
||||
String path = 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;
|
||||
return fs().getFile(getCanonicalPathImpl());
|
||||
}
|
||||
|
||||
VirtualFile findParentFile() {
|
||||
|
@ -631,6 +586,6 @@ public class TFile implements Serializable, Comparable<TFile> {
|
|||
if (path.isEmpty() || path.equals("/")) {
|
||||
return null;
|
||||
}
|
||||
return new TFile(getCanonicalPathImpl()).getParentFile().findVirtualFile();
|
||||
return new TFile(path).getParentFile().findVirtualFile();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class TFileInputStream extends InputStream {
|
|||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
accessor = virtualFile.createAccessor();
|
||||
accessor = virtualFile.createAccessor(true, false, false);
|
||||
if (accessor == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
|
|
@ -43,28 +43,23 @@ public class TFileOutputStream extends OutputStream {
|
|||
if (file.getName().isEmpty()) {
|
||||
throw new FileNotFoundException("Invalid file name");
|
||||
}
|
||||
VirtualFile virtualFile = file.findVirtualFile();
|
||||
if (virtualFile == null) {
|
||||
VirtualFile parentVirtualFile = file.findParentFile();
|
||||
if (parentVirtualFile != null && parentVirtualFile.isDirectory()) {
|
||||
virtualFile = parentVirtualFile.createFile(file.getName());
|
||||
}
|
||||
}
|
||||
if (virtualFile == null || virtualFile.isDirectory()) {
|
||||
if (parentVirtualFile != null) {
|
||||
try {
|
||||
parentVirtualFile.createFile(file.getName());
|
||||
} catch (IOException e) {
|
||||
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) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
ensureOpened();
|
||||
ensurePos();
|
||||
accessor.write(pos, b, off, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
ensureOpened();
|
||||
accessor.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (accessor != null) {
|
||||
accessor.close();
|
||||
}
|
||||
accessor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
ensureOpened();
|
||||
ensurePos();
|
||||
byte[] buffer = { (byte) b };
|
||||
accessor.write(pos, buffer, 0, 1);
|
||||
pos++;
|
||||
|
@ -101,4 +101,11 @@ public class TFileOutputStream extends OutputStream {
|
|||
throw new IOException("This stream is already closed");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensurePos() throws IOException {
|
||||
ensureOpened();
|
||||
if (pos < 0) {
|
||||
pos = accessor.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.teavm.classlib.java.lang.TNullPointerException;
|
|||
|
||||
public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
||||
private boolean readOnly;
|
||||
private boolean autoFlush;
|
||||
private VirtualFileAccessor accessor;
|
||||
private int pos;
|
||||
private byte[] buff;
|
||||
|
@ -44,8 +45,10 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
|||
readOnly = true;
|
||||
break;
|
||||
case "rw":
|
||||
break;
|
||||
case "rwd":
|
||||
case "rws":
|
||||
autoFlush = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid mode: " + mode);
|
||||
|
@ -56,10 +59,11 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
|||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
accessor = virtualFile.createAccessor();
|
||||
accessor = virtualFile.createAccessor(true, !readOnly, false);
|
||||
if (accessor == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
buff = new byte[16];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -281,20 +285,17 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
|||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
if (readOnly) {
|
||||
throw new IOException("This instance is read-only");
|
||||
}
|
||||
ensureOpened();
|
||||
byte[] buffer = { (byte) b };
|
||||
accessor.write(pos, buffer, 0, 1);
|
||||
if (autoFlush) {
|
||||
accessor.flush();
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
if (readOnly) {
|
||||
throw new IOException("This instance is read-only");
|
||||
}
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
|
@ -306,6 +307,9 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
|||
}
|
||||
ensureOpened();
|
||||
accessor.write(pos, b, off, len);
|
||||
if (autoFlush) {
|
||||
accessor.flush();
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
||||
|
@ -456,7 +460,7 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
|||
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;
|
||||
return offset;
|
||||
|
@ -478,7 +482,7 @@ public class TRandomAccessFile implements DataInput, DataOutput, Closeable {
|
|||
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();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int charValue = str.charAt(i);
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
|
|||
import org.teavm.classlib.impl.unicode.UnicodeHelper;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.c.Include;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.metadata.StringResource;
|
||||
|
@ -244,6 +245,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
|
|||
|
||||
@Import(module = "teavm", name = "towlower")
|
||||
@Include("wctype.h")
|
||||
@Unmanaged
|
||||
private static native int toLowerCaseSystem(int codePoint);
|
||||
|
||||
public static char toUpperCase(char ch) {
|
||||
|
@ -261,6 +263,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
|
|||
|
||||
@Import(module = "teavm", name = "towupper")
|
||||
@Include("wctype.h")
|
||||
@Unmanaged
|
||||
private static native int toUpperCaseSystem(int codePoint);
|
||||
|
||||
public static int digit(char ch, int radix) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.io.IOException;
|
|||
import org.teavm.classlib.java.io.TOutputStream;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.c.Include;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
|
@ -38,5 +39,6 @@ class TConsoleOutputStreamStderr extends TOutputStream {
|
|||
|
||||
@Include("wchar.h")
|
||||
@Import(name = "putwchar", module = "teavm")
|
||||
@Unmanaged
|
||||
static native void writeImpl(int b);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
|
|||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
@NoSideEffects
|
||||
|
@ -233,21 +234,25 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
@JSBody(params = "v", script = "return isNaN(v);")
|
||||
@Import(module = "teavm", name = "isnan")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native boolean isNaN(double v);
|
||||
|
||||
@JSBody(script = "return NaN;")
|
||||
@Import(module = "teavm", name = "teavm_getNaN")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native double getNaN();
|
||||
|
||||
@JSBody(params = "v", script = "return !isFinite(v);")
|
||||
@Import(module = "teavm", name = "isinf")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native boolean isInfinite(double v);
|
||||
|
||||
@JSBody(params = "v", script = "return isFinite(v);")
|
||||
@Import(module = "teavm", name = "isfinite")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native boolean isFinite(double v);
|
||||
|
||||
public static long doubleToRawLongBits(double value) {
|
||||
|
@ -257,11 +262,13 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
@InjectedBy(DoubleGenerator.class)
|
||||
@Import(name = "teavm_reinterpretDoubleToLong")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native long doubleToLongBits(double value);
|
||||
|
||||
@InjectedBy(DoubleGenerator.class)
|
||||
@Import(name = "teavm_reinterpretLongToDouble")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native double longBitsToDouble(long bits);
|
||||
|
||||
public static String toHexString(double d) {
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
|
|||
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
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);")
|
||||
@Import(module = "teavm", name = "isnan")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native boolean isNaN(float v);
|
||||
|
||||
@JSBody(params = "v", script = "return !isFinite(v);")
|
||||
@Import(module = "teavm", name = "isinf")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native boolean isInfinite(float v);
|
||||
|
||||
@JSBody(params = "v", script = "return isFinite(v);")
|
||||
@Import(module = "teavm", name = "isfinite")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native boolean isFinite(float v);
|
||||
|
||||
@JSBody(script = "return NaN;")
|
||||
@Import(module = "teavm", name = "teavm_getNaN")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native float getNaN();
|
||||
|
||||
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);")
|
||||
@Import(name = "teavm_reinterpretFloatToInt")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native int floatToIntBits(float value);
|
||||
|
||||
@JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);")
|
||||
@Import(name = "teavm_reinterpretIntToFloat")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
public static native float intBitsToFloat(int bits);
|
||||
|
||||
public static String toHexString(float f) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
|
|||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
||||
@NoSideEffects
|
||||
public final class TMath extends TObject {
|
||||
|
@ -29,26 +30,32 @@ public final class TMath extends TObject {
|
|||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "sin")
|
||||
@Unmanaged
|
||||
public static native double sin(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "cos")
|
||||
@Unmanaged
|
||||
public static native double cos(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "tan")
|
||||
@Unmanaged
|
||||
public static native double tan(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "asin")
|
||||
@Unmanaged
|
||||
public static native double asin(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "acos")
|
||||
@Unmanaged
|
||||
public static native double acos(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "atan")
|
||||
@Unmanaged
|
||||
public static native double atan(double a);
|
||||
|
||||
public static double toRadians(double angdeg) {
|
||||
|
@ -61,10 +68,12 @@ public final class TMath extends TObject {
|
|||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "exp")
|
||||
@Unmanaged
|
||||
public static native double exp(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "log")
|
||||
@Unmanaged
|
||||
public static native double log(double a);
|
||||
|
||||
public static double log10(double a) {
|
||||
|
@ -73,6 +82,7 @@ public final class TMath extends TObject {
|
|||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "sqrt")
|
||||
@Unmanaged
|
||||
public static native double sqrt(double a);
|
||||
|
||||
public static double cbrt(double a) {
|
||||
|
@ -86,14 +96,17 @@ public final class TMath extends TObject {
|
|||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "ceil")
|
||||
@Unmanaged
|
||||
public static native double ceil(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "floor")
|
||||
@Unmanaged
|
||||
public static native double floor(double a);
|
||||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "pow")
|
||||
@Unmanaged
|
||||
public static native double pow(double x, double y);
|
||||
|
||||
public static double rint(double a) {
|
||||
|
@ -102,6 +115,7 @@ public final class TMath extends TObject {
|
|||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "atan2")
|
||||
@Unmanaged
|
||||
public static native double atan2(double y, double x);
|
||||
|
||||
public static int round(float a) {
|
||||
|
@ -114,6 +128,7 @@ public final class TMath extends TObject {
|
|||
|
||||
@GeneratedBy(MathNativeGenerator.class)
|
||||
@Import(module = "teavmMath", name = "random")
|
||||
@Unmanaged
|
||||
public static native double random();
|
||||
|
||||
public static int min(int a, int b) {
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.util.Enumeration;
|
|||
import java.util.Properties;
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
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.TInputStream;
|
||||
import org.teavm.classlib.java.io.TPrintStream;
|
||||
|
@ -154,11 +156,28 @@ public final class TSystem extends TObject {
|
|||
defaults.put("line.separator", lineSeparator());
|
||||
defaults.put("java.io.tmpdir", "/tmp");
|
||||
defaults.put("java.vm.version", "1.8");
|
||||
defaults.put("user.home", "/");
|
||||
defaults.put("user.home", getHomeDir());
|
||||
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) {
|
||||
initPropertiesIfNeeded();
|
||||
return properties.getProperty(key);
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.teavm.classlib.java.lang.TComparable;
|
|||
import org.teavm.classlib.java.lang.TSystem;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.core.JSDate;
|
||||
|
||||
public class TDate implements TComparable<TDate> {
|
||||
|
@ -34,6 +35,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_init")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native void initLowLevel();
|
||||
|
||||
public TDate() {
|
||||
|
@ -66,6 +68,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_create")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native long initDateLowLevel(int year, int month, int date, int hrs, int min, int sec);
|
||||
|
||||
public TDate(String s) {
|
||||
|
@ -88,6 +91,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_createUtc")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native long initUtcDateLowLevel(int year, int month, int date, int hrs, int min, int sec);
|
||||
|
||||
@Deprecated
|
||||
|
@ -105,6 +109,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_parse")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native long parseLowLevel(String s);
|
||||
|
||||
@Deprecated
|
||||
|
@ -117,6 +122,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_getYear")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int getYearLowLevel(long date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -132,6 +138,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_setYear")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native long setYearLowLevel(long date, int year);
|
||||
|
||||
@Deprecated
|
||||
|
@ -144,6 +151,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_getMonth")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int getMonthLowLevel(long date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -159,6 +167,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_setMonth")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native long setMonthLowLevel(long date, int month);
|
||||
|
||||
@Deprecated
|
||||
|
@ -171,6 +180,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_getDate")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int getDateLowLevel(long date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -186,6 +196,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_setDate")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int setDateLowLevel(long target, int date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -197,6 +208,7 @@ public class TDate implements TComparable<TDate> {
|
|||
}
|
||||
|
||||
@Import(name = "teavm_date_getDay")
|
||||
@Unmanaged
|
||||
public static native int getDayLowLevel(long date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -224,6 +236,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_setHours")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int setHoursLowLevel(long date, int hours);
|
||||
|
||||
@Deprecated
|
||||
|
@ -236,6 +249,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_getMinutes")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int getMinutesLowLevel(long date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -251,6 +265,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_setMinutes")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int setMinutesLowLevel(long date, int minutes);
|
||||
|
||||
@Deprecated
|
||||
|
@ -263,6 +278,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_getSeconds")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int getSecondsLowLevel(long date);
|
||||
|
||||
@Deprecated
|
||||
|
@ -278,6 +294,7 @@ public class TDate implements TComparable<TDate> {
|
|||
|
||||
@Import(name = "teavm_date_setSeconds")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int setSecondsLowLevel(long date, int seconds);
|
||||
|
||||
public long getTime() {
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.teavm.classlib.java.io.TSerializable;
|
|||
import org.teavm.classlib.java.lang.TMath;
|
||||
import org.teavm.classlib.java.lang.TObject;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
public class TRandom extends TObject implements TSerializable {
|
||||
|
@ -82,6 +83,7 @@ public class TRandom extends TObject implements TSerializable {
|
|||
}
|
||||
|
||||
@Import(name = "teavm_rand")
|
||||
@Unmanaged
|
||||
private static native double crand();
|
||||
|
||||
/**
|
||||
|
@ -119,5 +121,6 @@ public class TRandom extends TObject implements TSerializable {
|
|||
|
||||
@JSBody(script = "return Math.random();")
|
||||
@Import(module = "teavmMath", name = "random")
|
||||
@Unmanaged
|
||||
private static native double random();
|
||||
}
|
||||
|
|
|
@ -379,6 +379,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
copyResource("stringhash.c", buildTarget);
|
||||
copyResource("references.c", buildTarget);
|
||||
copyResource("date.c", buildTarget);
|
||||
copyResource("file.c", buildTarget);
|
||||
generateCallSites(buildTarget, context, classes.getClassNames());
|
||||
generateStrings(buildTarget, context);
|
||||
|
||||
|
@ -681,6 +682,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
files.add("callsites.c");
|
||||
files.add("references.c");
|
||||
files.add("date.c");
|
||||
files.add("file.c");
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
files.add(ClassGenerator.fileName(className) + ".c");
|
||||
|
|
|
@ -101,7 +101,7 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
context.writer().print(")");
|
||||
break;
|
||||
case "getChar":
|
||||
context.writer().print("((int32_t) *(uchar16_t*) ");
|
||||
context.writer().print("((int32_t) *(char16_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
|
@ -136,9 +136,9 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
context.writer().print(")");
|
||||
break;
|
||||
case "putChar":
|
||||
context.writer().print("(*(uchar16_t*) ");
|
||||
context.writer().print("(*(char16_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" = (uchar16_t) ");
|
||||
context.writer().print(" = (char16_t) ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
|
@ -225,6 +225,7 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
case BYTE:
|
||||
return 1;
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
return 2;
|
||||
case INTEGER:
|
||||
case FLOAT:
|
||||
|
|
|
@ -43,6 +43,23 @@ inline static int64_t teavm_date_timestamp(struct tm *t) {
|
|||
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) {
|
||||
*t = teavm_epochStartTm;
|
||||
t->tm_sec += timestamp / 1000;
|
||||
|
|
309
core/src/main/resources/org/teavm/backend/c/file.c
Normal file
309
core/src/main/resources/org/teavm/backend/c/file.c
Normal 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
|
|
@ -14,6 +14,7 @@
|
|||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -64,6 +65,8 @@ void teavm_beforeInit() {
|
|||
srand(time(NULL));
|
||||
|
||||
#ifdef __GNUC__
|
||||
setlocale (LC_ALL, "");
|
||||
|
||||
struct sigaction sigact;
|
||||
sigact.sa_flags = 0;
|
||||
sigact.sa_handler = NULL;
|
||||
|
@ -377,6 +380,42 @@ TeaVM_String* teavm_cToString(char* cstring) {
|
|||
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* array = teavm_allocateStringArray(argc - 1);
|
||||
TeaVM_String** arrayData = TEAVM_ARRAY_DATA(array, TeaVM_String*);
|
||||
|
@ -438,3 +477,25 @@ void teavm_afterInitClasses() {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -207,6 +207,8 @@ extern int32_t teavm_timeZoneOffset();
|
|||
|
||||
extern char* teavm_stringToC(void*);
|
||||
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) {
|
||||
if (s != NULL) {
|
||||
free(s);
|
||||
|
@ -238,12 +240,12 @@ extern void teavm_waitFor(int64_t timeout);
|
|||
extern void teavm_interrupt();
|
||||
|
||||
extern void teavm_outOfMemory();
|
||||
extern void teavm_printString(char* s);
|
||||
extern void teavm_printInt(int32_t i);
|
||||
extern void teavm_printString(char*);
|
||||
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*);
|
||||
|
||||
|
@ -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_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_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 int64_t teavm_date_setYear(int64_t,int32_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 int64_t teavm_date_setSeconds(int64_t,int32_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);
|
119
core/src/main/resources/org/teavm/backend/c/utf8.c
Normal file
119
core/src/main/resources/org/teavm/backend/c/utf8.c
Normal 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);
|
||||
}
|
|
@ -189,10 +189,11 @@ public class FileOutputStreamTest {
|
|||
f = new File(System.getProperty("user.home"), "output.tst");
|
||||
fos = new FileOutputStream(f.getPath());
|
||||
fos.write(fileString.getBytes());
|
||||
fos.close();
|
||||
fis = new FileInputStream(f.getPath());
|
||||
byte[] rbytes = new byte[4000];
|
||||
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
|
||||
|
@ -200,10 +201,11 @@ public class FileOutputStreamTest {
|
|||
f = new File(System.getProperty("user.home"), "output.tst");
|
||||
fos = new FileOutputStream(f.getPath());
|
||||
fos.write(fileString.getBytes(), 0, fileString.length());
|
||||
fos.close();
|
||||
fis = new FileInputStream(f.getPath());
|
||||
byte[] rbytes = new byte[4000];
|
||||
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
|
||||
File file = new File("FileOutputStream.tmp");
|
||||
|
@ -224,6 +226,7 @@ public class FileOutputStreamTest {
|
|||
f = new File(System.getProperty("user.home"), "output.tst");
|
||||
fos = new FileOutputStream(f.getPath());
|
||||
fos.write('t');
|
||||
fos.close();
|
||||
fis = new FileInputStream(f.getPath());
|
||||
assertEquals("Incorrect char written", 't', fis.read());
|
||||
}
|
||||
|
|
|
@ -1323,7 +1323,7 @@ public class FileTest {
|
|||
// it a new directory name.)
|
||||
if (baseDir.exists()) {
|
||||
dirNumber++;
|
||||
baseDir = new File(base, String.valueOf(dirNumber));
|
||||
baseDir = new File(base, platformId + String.valueOf(dirNumber));
|
||||
} else {
|
||||
dirExists = false;
|
||||
}
|
||||
|
|
|
@ -56,10 +56,11 @@ class CRunStrategy implements TestRunStrategy {
|
|||
writeLines(compilerOutput);
|
||||
|
||||
List<String> runtimeOutput = new ArrayList<>();
|
||||
List<String> stdout = new ArrayList<>();
|
||||
outputFile.setExecutable(true);
|
||||
runProcess(new ProcessBuilder(outputFile.getPath()).start(), runtimeOutput);
|
||||
if (!runtimeOutput.isEmpty() && runtimeOutput.get(runtimeOutput.size() - 1).equals("SUCCESS")) {
|
||||
writeLines(runtimeOutput.subList(0, runtimeOutput.size() - 1));
|
||||
runProcess(new ProcessBuilder(outputFile.getPath()).start(), runtimeOutput, stdout);
|
||||
if (!stdout.isEmpty() && stdout.get(stdout.size() - 1).equals("SUCCESS")) {
|
||||
writeLines(runtimeOutput);
|
||||
run.getCallback().complete();
|
||||
} else {
|
||||
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();
|
||||
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 stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
ConcurrentLinkedQueue<String> lines = new ConcurrentLinkedQueue<>();
|
||||
|
@ -109,7 +111,6 @@ class CRunStrategy implements TestRunStrategy {
|
|||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
thread = new Thread(() -> {
|
||||
try {
|
||||
while (true) {
|
||||
String line = stdin.readLine();
|
||||
|
@ -117,13 +118,11 @@ class CRunStrategy implements TestRunStrategy {
|
|||
break;
|
||||
}
|
||||
lines.add(line);
|
||||
stdout.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
boolean result = process.waitFor() == 0;
|
||||
output.addAll(lines);
|
||||
|
|
|
@ -101,6 +101,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
private static final String C_ENABLED = "teavm.junit.c";
|
||||
private static final String WASM_ENABLED = "teavm.junit.wasm";
|
||||
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 OPTIMIZED = "teavm.junit.optimized";
|
||||
private static final String FAST_ANALYSIS = "teavm.junit.fastAnalysis";
|
||||
|
@ -607,10 +608,16 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
File path) {
|
||||
return compileTest(method, configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path,
|
||||
|
|
Loading…
Reference in New Issue
Block a user