Compare commits

...

11 Commits

14 changed files with 110 additions and 86 deletions

View File

@@ -1,4 +1,4 @@
project_group = me.topchetoeu
project_name = jscript
project_version = 0.9.27-beta
project_version = 0.9.41-beta
main_class = me.topchetoeu.jscript.utils.JScriptRepl

View File

@@ -10,7 +10,7 @@ public class ContinueStatement extends Statement {
@Override
public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.nop(loc(), "cont", label));
target.add(Instruction.nop("cont", label));
if (pollute) target.add(Instruction.pushUndefined());
}

View File

@@ -51,7 +51,7 @@ public class FilesystemLib {
try {
if (fs.stat(path).type != EntryType.FILE) {
throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a file").setAction(ActionType.OPEN);
throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a file").setAction(ActionType.OPEN).setPath(path);
}
return new FileLib(fs.open(path, _mode));

View File

@@ -1,5 +1,7 @@
package me.topchetoeu.jscript.lib;
import java.text.NumberFormat;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
@@ -85,6 +87,15 @@ public class NumberLib {
@Expose public static String __toString(Arguments args) {
return Values.toString(args.ctx, args.self);
}
@Expose public static String __toFixed(Arguments args) {
var digits = args.getInt(0, 0);
var nf = NumberFormat.getNumberInstance();
nf.setMinimumFractionDigits(digits);
nf.setMaximumFractionDigits(digits);
return nf.format(args.getDouble(-1));
}
@Expose public static double __valueOf(Arguments args) {
if (Values.isWrapper(args.self, NumberLib.class)) return Values.wrapper(args.self, NumberLib.class).value;
else return Values.toNumber(args.ctx, args.self);

View File

@@ -14,43 +14,26 @@ public class Context implements Extensions {
public final Context parent;
public final Extensions extensions;
public final Frame frame;
// public final Engine engine;
public final int stackSize;
@Override public <T> void add(Key<T> key, T obj) {
if (extensions != null) extensions.add(key, obj);
// else if (engine != null) engine.add(key, obj);
}
@Override public <T> T get(Key<T> key) {
if (extensions != null && extensions.has(key)) return extensions.get(key);
// else if (engine != null && engine.has(key)) return engine.get(key);
return null;
}
@Override public boolean has(Key<?> key) {
return
extensions != null && extensions.has(key);
// engine != null && engine.has(key);
return extensions != null && extensions.has(key);
}
@Override public boolean remove(Key<?> key) {
var res = false;
if (extensions != null) res |= extensions.remove(key);
// else if (engine != null) res |= engine.remove(key);
return res;
}
@Override public Iterable<Key<?>> keys() {
if (extensions == null) return List.of();
else return extensions.keys();
// if (engine == null && environment == null) return List.of();
// if (engine == null) return environment.keys();
// if (environment == null) return engine.keys();
// return () -> Stream.concat(
// StreamSupport.stream(engine.keys().spliterator(), false),
// StreamSupport.stream(environment.keys().spliterator(), false)
// ).distinct().iterator();
}
public FunctionValue compile(Filename filename, String raw) {
@@ -101,7 +84,7 @@ public class Context implements Extensions {
this(null, null, null, 0);
}
public Context(Extensions ext) {
this(null, ext, null, 0);
this(null, clean(ext), null, 0);
}
public static Context of(Extensions ext) {
@@ -109,7 +92,7 @@ public class Context implements Extensions {
return new Context(ext);
}
public static Extensions clean(Extensions ext) {
if (ext instanceof Context) return ((Context)ext).extensions;
if (ext instanceof Context) return clean(((Context)ext).extensions);
else return ext;
}
}

View File

@@ -20,7 +20,7 @@ public class GlobalScope {
return Values.hasMember(ext, obj, name, false);
}
public GlobalScope globalChild() {
public GlobalScope child() {
var obj = new ObjectValue();
Values.setPrototype(null, obj, this.obj);
return new GlobalScope(obj);

View File

@@ -44,7 +44,7 @@ public class CodeFunction extends FunctionValue {
public CodeFunction(Extensions extensions, String name, FunctionBody body, ValueVariable[] captures) {
super(name, body.argsN);
this.captures = captures;
this.extensions = extensions;
this.extensions = Context.clean(extensions);
this.body = body;
}
}

View File

@@ -89,7 +89,6 @@ public class JScriptRepl {
private static void initEnv() {
environment = Internals.apply(environment);
var wp = NativeWrapperProvider.get(environment);
var glob = GlobalScope.get(environment);
glob.define(null, false, new NativeFunction("exit", args -> {

View File

@@ -397,7 +397,7 @@ public class SimpleDebugger implements Debugger {
catch (Exception e) { }
try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(ctx, obj) + " }")); }
catch (EngineException e) { }
catch (Exception e) { }
}

View File

@@ -32,7 +32,7 @@ public abstract class BaseFile<T> implements File {
@Override public synchronized long seek(long offset, int pos) {
try {
if (handle == null) throw new FilesystemException(ErrorReason.CLOSED);
if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for seeking.");
if (mode == Mode.NONE) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for seeking.");
return onSeek(offset, pos);
}
catch (FilesystemException e) { throw e.setAction(ActionType.SEEK); }

View File

@@ -54,16 +54,12 @@ public class FilesystemException extends RuntimeException {
@Override public String getMessage() {
var parts = new ArrayList<String>(10);
path = String.join(" ", parts).trim();
if (path.isEmpty()) path = null;
parts.clear();
parts.add(action == null ? "An action performed upon " : action.readable(reason.usePast));
if (entry == EntryType.FILE) parts.add("file");
if (entry == EntryType.FOLDER) parts.add("folder");
if (path != null) parts.add(path);
if (path != null && !path.isBlank()) parts.add(path.trim());
parts.add(reason.readable);

View File

@@ -1,5 +1,6 @@
package me.topchetoeu.jscript.utils.filesystem;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
@@ -35,7 +36,8 @@ public class PhysicalFilesystem implements Filesystem {
));
else return handles.put(new PhysicalFile(path, perms));
}
catch (IOException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); }
catch (FileNotFoundException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); }
catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION); }
}
catch (FilesystemException e) { throw e.setAction(ActionType.OPEN).setPath(_path); }
}
@@ -68,12 +70,7 @@ public class PhysicalFilesystem implements Filesystem {
if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE);
var perms = Mode.NONE;
if (Files.isReadable(path)) {
if (Files.isWritable(path)) perms = Mode.READ_WRITE;
else perms = Mode.READ;
}
var perms = Mode.of(Files.isReadable(path), Files.isWritable(path));
if (perms == Mode.NONE) return new FileStat(Mode.NONE, EntryType.NONE);

View File

@@ -4,10 +4,12 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.runtime.Context;
@@ -30,6 +32,7 @@ public class NativeWrapperProvider implements Copyable {
private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>();
private final HashMap<Class<?>, Class<?>> classToProxy = new HashMap<>();
private final HashMap<Class<?>, Class<?>> proxyToClass = new HashMap<>();
private final HashMap<Class<?>, Class<?>> interfaceToProxy = new HashMap<>();
private final HashSet<Class<?>> ignore = new HashSet<>();
private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) {
@@ -83,10 +86,16 @@ public class NativeWrapperProvider implements Copyable {
String.join(", ", Arrays.stream(actual).map(v -> v.getName()).collect(Collectors.toList()))
));
}
private static String getName(Class<?> clazz) {
private static String getName(Class<?> ...classes) {
String last = null;
for (var clazz : classes) {
var classNat = clazz.getAnnotation(WrapperName.class);
if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim();
else return clazz.getSimpleName();
else if (last != null) last = clazz.getSimpleName();
}
return last;
}
private static void checkUnderscore(Member member) {
@@ -120,13 +129,13 @@ public class NativeWrapperProvider implements Copyable {
for (var method : clazz.getDeclaredMethods()) {
for (var annotation : method.getAnnotationsByType(Expose.class)) {
any = true;
if (!annotation.target().shouldApply(target)) continue;
checkUnderscore(method);
var name = getName(method, annotation.value());
var key = getKey(name);
var repeat = false;
any = true;
switch (annotation.type()) {
case METHOD:
@@ -162,13 +171,13 @@ public class NativeWrapperProvider implements Copyable {
));
}
for (var annotation : method.getAnnotationsByType(ExposeField.class)) {
any = true;
if (!annotation.target().shouldApply(target)) continue;
checkUnderscore(method);
var name = getName(method, annotation.value());
var key = getKey(name);
var repeat = false;
any = true;
if (props.contains(key) || nonProps.contains(key)) repeat = true;
else {
@@ -191,13 +200,13 @@ public class NativeWrapperProvider implements Copyable {
}
for (var field : clazz.getDeclaredFields()) {
for (var annotation : field.getAnnotationsByType(ExposeField.class)) {
any = true;
if (!annotation.target().shouldApply(target)) continue;
checkUnderscore(field);
var name = getName(field, annotation.value());
var key = getKey(name);
var repeat = false;
any = true;
if (props.contains(key) || nonProps.contains(key)) repeat = true;
else {
@@ -237,13 +246,24 @@ public class NativeWrapperProvider implements Copyable {
return any;
}
private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> ...appliers) {
var res = false;
private static Method getConstructor(Class<?> clazz) {
for (var i = appliers.length - 1; i >= 0; i--) {
res |= apply(obj, target, appliers[i]);
}
return res;
}
private static Method getConstructor(Class<?> ...appliers) {
for (var clazz : appliers) {
for (var method : clazz.getDeclaredMethods()) {
if (!method.isAnnotationPresent(ExposeConstructor.class)) continue;
checkSignature(method, true, Arguments.class);
return method;
}
}
return null;
}
@@ -254,10 +274,10 @@ public class NativeWrapperProvider implements Copyable {
* All accessors and methods will expect the this argument to be a native wrapper of the given class type.
* @param clazz The class for which a prototype should be generated
*/
public static ObjectValue makeProto(Class<?> clazz) {
public static ObjectValue makeProto(Class<?> ...appliers) {
var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
if (!apply(res, ExposeTarget.PROTOTYPE, clazz)) return null;
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(appliers));
if (!apply(res, ExposeTarget.PROTOTYPE, appliers)) return null;
return res;
}
/**
@@ -266,14 +286,14 @@ public class NativeWrapperProvider implements Copyable {
* When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible.
* @param clazz The class for which a constructor should be generated
*/
public static FunctionValue makeConstructor(Class<?> clazz) {
var constr = getConstructor(clazz);
public static FunctionValue makeConstructor(Class<?> ...appliers) {
var constr = getConstructor(appliers);
FunctionValue res = constr == null ?
new NativeFunction(getName(clazz), args -> { throw EngineException.ofError("This constructor is not invokable."); }) :
create(getName(clazz), constr);
new NativeFunction(getName(appliers), args -> { throw EngineException.ofError("This constructor is not invokable."); }) :
create(getName(appliers), constr);
apply(res, ExposeTarget.CONSTRUCTOR, clazz);
if (!apply(res, ExposeTarget.CONSTRUCTOR, appliers) && constr == null) return null;
return res;
}
@@ -283,13 +303,27 @@ public class NativeWrapperProvider implements Copyable {
* This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead.
* @param clazz The class for which a constructor should be generated
*/
public static ObjectValue makeNamespace(Class<?> clazz) {
public static ObjectValue makeNamespace(Class<?> ...appliers) {
var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
if (!apply(res, ExposeTarget.NAMESPACE, clazz)) return null;
if (!apply(res, ExposeTarget.NAMESPACE, appliers)) return null;
return res;
}
private Class<?>[] getAppliers(Class<?> clazz) {
var res = new ArrayList<Class<?>>();
res.add(clazz);
if (classToProxy.containsKey(clazz)) res.add(classToProxy.get(clazz));
for (var intf : interfaceToProxy.keySet()) {
if (intf.isAssignableFrom(clazz)) res.add(interfaceToProxy.get(intf));
}
return res.toArray(Class<?>[]::new);
}
private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
var parent = clazz;
@@ -326,8 +360,10 @@ public class NativeWrapperProvider implements Copyable {
clazz.isSynthetic()
) return;
if (constr == null) constr = makeConstructor(clazz);
if (proto == null) proto = makeProto(clazz);
var appliers = getAppliers(clazz);
if (constr == null) constr = makeConstructor(appliers);
if (proto == null) proto = makeProto(appliers);
if (constr == null || proto == null) return;
@@ -386,30 +422,29 @@ public class NativeWrapperProvider implements Copyable {
public void set(Class<?> clazz, Class<?> wrapper) {
if (clazz == null) return;
if (wrapper == null) wrapper = clazz;
if (classToProxy.get(clazz) == wrapper) return;
classToProxy.remove(wrapper);
proxyToClass.remove(clazz);
classToProxy.put(clazz, wrapper);
proxyToClass.put(wrapper, clazz);
ignore.remove(clazz);
var proto = makeProto(wrapper);
var constr = makeConstructor(wrapper);
prototypes.put(clazz, proto);
constructors.put(clazz, constr);
proto.defineProperty(null, "constructor", constr, true, false, false);
constr.defineProperty(null, "prototype", proto, true, false, false);
for (var el : prototypes.keySet()) {
if (clazz.isAssignableFrom(el)) {
updateProtoChain(el, getProto(el), getConstr(el));
if (clazz.isInterface()) {
if (wrapper == null || wrapper == clazz) interfaceToProxy.remove(clazz);
else interfaceToProxy.put(clazz, wrapper);
}
else {
if (wrapper == null || wrapper == clazz) classToProxy.remove(clazz);
else classToProxy.put(clazz, wrapper);
}
var classes = Stream.concat(
Stream.of(clazz),
prototypes.keySet().stream().filter(clazz::isAssignableFrom)
).toArray(Class<?>[]::new);
for (var el : classes) {
prototypes.remove(el);
constructors.remove(el);
namespaces.remove(el);
}
for (var el : classes) {
initType(el, null, null);
}
}

View File

@@ -6,6 +6,7 @@ import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.Mode;
@@ -25,8 +26,10 @@ public interface ModuleRepo {
if (modules.containsKey(name)) return modules.get(name);
var env = ctx.extensions.child();
var env = Context.clean(ctx.extensions).child();
env.add(CWD, fs.normalize(name, ".."));
var glob = env.get(GlobalScope.KEY);
env.add(GlobalScope.KEY, glob.child());
var mod = new SourceModule(filename, src, env);
modules.put(name, mod);