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 {
|
interface File {
|
||||||
readonly pointer: Promise<number>;
|
readonly pointer: Promise<number>;
|
||||||
readonly length: Promise<number>;
|
readonly length: Promise<number>;
|
||||||
readonly mode: Promise<'' | 'r' | 'rw'>;
|
|
||||||
|
|
||||||
read(n: number): Promise<number[]>;
|
read(n: number): Promise<number[]>;
|
||||||
write(buff: number[]): Promise<void>;
|
write(buff: number[]): Promise<void>;
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
setPointer(val: number): Promise<void>;
|
seek(offset: number, whence: number): Promise<void>;
|
||||||
}
|
}
|
||||||
interface Filesystem {
|
interface Filesystem {
|
||||||
|
readonly SEEK_SET: 0;
|
||||||
|
readonly SEEK_CUR: 1;
|
||||||
|
readonly SEEK_END: 2;
|
||||||
|
|
||||||
open(path: string, mode: 'r' | 'rw'): Promise<File>;
|
open(path: string, mode: 'r' | 'rw'): Promise<File>;
|
||||||
ls(path: string): AsyncIterableIterator<string>;
|
ls(path: string): AsyncIterableIterator<string>;
|
||||||
mkdir(path: string): Promise<void>;
|
mkdir(path: string): Promise<void>;
|
||||||
@ -503,6 +506,7 @@ interface Filesystem {
|
|||||||
rm(path: string, recursive?: boolean): Promise<void>;
|
rm(path: string, recursive?: boolean): Promise<void>;
|
||||||
stat(path: string): Promise<FileStat>;
|
stat(path: string): Promise<FileStat>;
|
||||||
exists(path: string): Promise<boolean>;
|
exists(path: string): Promise<boolean>;
|
||||||
|
normalize(...paths: string[]): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Encoding {
|
interface Encoding {
|
||||||
@ -526,6 +530,7 @@ declare var parseInt: typeof Number.parseInt;
|
|||||||
declare var parseFloat: typeof Number.parseFloat;
|
declare var parseFloat: typeof Number.parseFloat;
|
||||||
|
|
||||||
declare function log(...vals: any[]): void;
|
declare function log(...vals: any[]): void;
|
||||||
|
declare function require(name: string): any;
|
||||||
|
|
||||||
declare var Array: ArrayConstructor;
|
declare var Array: ArrayConstructor;
|
||||||
declare var Boolean: BooleanConstructor;
|
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.Mode;
|
||||||
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
||||||
import me.topchetoeu.jscript.lib.Internals;
|
import me.topchetoeu.jscript.lib.Internals;
|
||||||
|
import me.topchetoeu.jscript.modules.ModuleRepo;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static class Printer implements Observer<Object> {
|
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("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() {
|
private static void initEngine() {
|
||||||
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
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.NativeGetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
|
import me.topchetoeu.jscript.modules.RootModuleRepo;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.permissions.Permission;
|
import me.topchetoeu.jscript.permissions.Permission;
|
||||||
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
||||||
|
|
||||||
|
// TODO: Remove hardcoded extensions form environment
|
||||||
public class Environment implements PermissionsProvider {
|
public class Environment implements PermissionsProvider {
|
||||||
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||||
|
|
||||||
@ -30,8 +32,11 @@ public class Environment implements PermissionsProvider {
|
|||||||
|
|
||||||
public GlobalScope global;
|
public GlobalScope global;
|
||||||
public WrappersProvider wrappers;
|
public WrappersProvider wrappers;
|
||||||
|
|
||||||
public PermissionsProvider permissions = null;
|
public PermissionsProvider permissions = null;
|
||||||
public final RootFilesystem filesystem = new RootFilesystem(this);
|
public final RootFilesystem filesystem = new RootFilesystem(this);
|
||||||
|
public final RootModuleRepo modules = new RootModuleRepo();
|
||||||
|
public String moduleCwd = "/";
|
||||||
|
|
||||||
private static int nextId = 0;
|
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);
|
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) {
|
@Native public ObjectValue proto(String name) {
|
||||||
return prototypes.get(name);
|
return prototypes.get(name);
|
||||||
}
|
}
|
||||||
@ -96,6 +96,9 @@ public class Environment implements PermissionsProvider {
|
|||||||
@Native public Environment child() {
|
@Native public Environment child() {
|
||||||
var res = fork();
|
var res = fork();
|
||||||
res.global = res.global.globalChild();
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +109,10 @@ public class Environment implements PermissionsProvider {
|
|||||||
return permissions == null || permissions.hasPermission(perm);
|
return permissions == null || permissions.hasPermission(perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Context context(Engine engine) {
|
||||||
|
return new Context(engine, this);
|
||||||
|
}
|
||||||
|
|
||||||
public static Symbol getSymbol(String name) {
|
public static Symbol getSymbol(String name) {
|
||||||
if (symbols.containsKey(name)) return symbols.get(name);
|
if (symbols.containsKey(name)) return symbols.get(name);
|
||||||
else {
|
else {
|
||||||
|
@ -20,6 +20,12 @@ public class Internals {
|
|||||||
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
||||||
private static final DataKey<Integer> I = 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) {
|
@Native public static Object log(Context ctx, Object ...args) {
|
||||||
for (var arg : 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