feat: implement basic module system
This commit is contained in:
parent
4bc363485f
commit
2f58f6b245
9
src/assets/js/lib.d.ts
vendored
9
src/assets/js/lib.d.ts
vendored
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
20
src/me/topchetoeu/jscript/modules/Module.java
Normal file
20
src/me/topchetoeu/jscript/modules/Module.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
32
src/me/topchetoeu/jscript/modules/ModuleRepo.java
Normal file
32
src/me/topchetoeu/jscript/modules/ModuleRepo.java
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
30
src/me/topchetoeu/jscript/modules/RootModuleRepo.java
Normal file
30
src/me/topchetoeu/jscript/modules/RootModuleRepo.java
Normal 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);
|
||||
}
|
||||
}
|
23
src/me/topchetoeu/jscript/modules/SourceModule.java
Normal file
23
src/me/topchetoeu/jscript/modules/SourceModule.java
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user