Module support #11

Merged
TopchetoEU merged 20 commits from TopchetoEU/modules into master 2023-12-26 12:20:55 +00:00
8 changed files with 133 additions and 8 deletions
Showing only changes of commit 2f58f6b245 - Show all commits

View File

@ -488,14 +488,17 @@ interface FileStat {
interface File {
readonly pointer: Promise<number>;
readonly length: Promise<number>;
readonly mode: Promise<'' | 'r' | 'rw'>;
read(n: number): Promise<number[]>;
write(buff: number[]): Promise<void>;
close(): Promise<void>;
setPointer(val: number): Promise<void>;
seek(offset: number, whence: number): Promise<void>;
}
interface Filesystem {
readonly SEEK_SET: 0;
readonly SEEK_CUR: 1;
readonly SEEK_END: 2;
open(path: string, mode: 'r' | 'rw'): Promise<File>;
ls(path: string): AsyncIterableIterator<string>;
mkdir(path: string): Promise<void>;
@ -503,6 +506,7 @@ interface Filesystem {
rm(path: string, recursive?: boolean): Promise<void>;
stat(path: string): Promise<FileStat>;
exists(path: string): Promise<boolean>;
normalize(...paths: string[]): string;
}
interface Encoding {
@ -526,6 +530,7 @@ declare var parseInt: typeof Number.parseInt;
declare var parseFloat: typeof Number.parseFloat;
declare function log(...vals: any[]): void;
declare function require(name: string): any;
declare var Array: ArrayConstructor;
declare var Boolean: BooleanConstructor;

View File

@ -22,6 +22,7 @@ import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
import me.topchetoeu.jscript.filesystem.Mode;
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.modules.ModuleRepo;
public class Main {
public static class Printer implements Observer<Object> {
@ -119,7 +120,8 @@ public class Main {
}));
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
environment.filesystem.protocols.put("file", new PhysicalFilesystem("."));
environment.modules.repos.put("file", ModuleRepo.ofFilesystem(environment.filesystem));
}
private static void initEngine() {
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));

View File

@ -18,10 +18,12 @@ import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeSetter;
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
import me.topchetoeu.jscript.modules.RootModuleRepo;
import me.topchetoeu.jscript.parsing.Parsing;
import me.topchetoeu.jscript.permissions.Permission;
import me.topchetoeu.jscript.permissions.PermissionsProvider;
// TODO: Remove hardcoded extensions form environment
public class Environment implements PermissionsProvider {
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
@ -30,8 +32,11 @@ public class Environment implements PermissionsProvider {
public GlobalScope global;
public WrappersProvider wrappers;
public PermissionsProvider permissions = null;
public final RootFilesystem filesystem = new RootFilesystem(this);
public final RootModuleRepo modules = new RootModuleRepo();
public String moduleCwd = "/";
private static int nextId = 0;
@ -63,11 +68,6 @@ public class Environment implements PermissionsProvider {
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
});
public Environment addData(Data data) {
this.data.addAll(data);
return this;
}
@Native public ObjectValue proto(String name) {
return prototypes.get(name);
}
@ -96,6 +96,9 @@ public class Environment implements PermissionsProvider {
@Native public Environment child() {
var res = fork();
res.global = res.global.globalChild();
res.permissions = this.permissions;
res.filesystem.protocols.putAll(this.filesystem.protocols);
res.modules.repos.putAll(this.modules.repos);
return res;
}
@ -106,6 +109,10 @@ public class Environment implements PermissionsProvider {
return permissions == null || permissions.hasPermission(perm);
}
public Context context(Engine engine) {
return new Context(engine, this);
}
public static Symbol getSymbol(String name) {
if (symbols.containsKey(name)) return symbols.get(name);
else {

View File

@ -20,6 +20,12 @@ public class Internals {
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
private static final DataKey<Integer> I = new DataKey<>();
@Native public static Object require(Context ctx, String name) {
var env = ctx.environment();
var res = env.modules.getModule(ctx, env.moduleCwd, name);
res.load(ctx);
return res.value();
}
@Native public static Object log(Context ctx, Object ...args) {
for (var arg : args) {

View File

@ -0,0 +1,20 @@
package me.topchetoeu.jscript.modules;
import me.topchetoeu.jscript.engine.Context;
public abstract class Module {
private Object value;
private boolean loaded;
public Object value() { return value; }
public boolean loaded() { return loaded; }
protected abstract Object onLoad(Context ctx);
public void load(Context ctx) {
if (loaded) return;
this.value = onLoad(ctx);
this.loaded = true;
}
}

View File

@ -0,0 +1,32 @@
package me.topchetoeu.jscript.modules;
import java.util.HashMap;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.filesystem.Filesystem;
import me.topchetoeu.jscript.filesystem.Mode;
public interface ModuleRepo {
public Module getModule(Context ctx, String cwd, String name);
public static ModuleRepo ofFilesystem(Filesystem fs) {
var modules = new HashMap<String, Module>();
return (ctx, cwd, name) -> {
name = fs.normalize(cwd, name);
var filename = Filename.parse(name);
var src = fs.open(name, Mode.READ).readToString();
if (modules.containsKey(name)) return modules.get(name);
var env = ctx.environment().child();
env.moduleCwd = fs.normalize(name, "..");
var mod = new SourceModule(filename, src, env);
modules.put(name, mod);
return mod;
};
}
}

View File

@ -0,0 +1,30 @@
package me.topchetoeu.jscript.modules;
import java.util.HashMap;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.exceptions.EngineException;
public class RootModuleRepo implements ModuleRepo {
public final HashMap<String, ModuleRepo> repos = new HashMap<>();
@Override
public Module getModule(Context ctx, String cwd, String name) {
var i = name.indexOf(":");
String repoName, modName;
if (i < 0) {
repoName = "file";
modName = name;
}
else {
repoName = name.substring(0, i);
modName = name.substring(i + 1);
}
var repo = repos.get(repoName);
if (repo == null) throw EngineException.ofError("ModuleError", "Couldn't find module repo '" + repoName + "'.");
return repo.getModule(ctx, cwd, modName);
}
}

View File

@ -0,0 +1,23 @@
package me.topchetoeu.jscript.modules;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
public class SourceModule extends Module {
public final Filename filename;
public final String source;
public final Environment env;
@Override
protected Object onLoad(Context ctx) {
var res = new Context(ctx.engine, env).compile(filename, source);
return res.call(ctx);
}
public SourceModule(Filename filename, String source, Environment env) {
this.filename = filename;
this.source = source;
this.env = env;
}
}