fix: environment forks fixes

This commit is contained in:
TopchetoEU 2024-04-02 18:05:20 +03:00
parent d8e46c3149
commit 48bd304c6e
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
5 changed files with 162 additions and 140 deletions

View File

@ -0,0 +1,18 @@
package me.topchetoeu.jscript.lib;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
public class ThrowableLib {
@Expose public static String __message(Arguments args) {
if (args.self instanceof Throwable) return ((Throwable)args.self).getMessage();
else return null;
}
@Expose public static String __name(Arguments args) {
return args.self.getClass().getSimpleName();
}
@Expose public static String __toString(Arguments args) {
return __name(args) + ": " + __message(args);
}
}

View File

@ -61,7 +61,7 @@ public class Environment implements Extensions {
} }
public Environment copy() { public Environment copy() {
var res = new Environment(null, global); var res = new Environment();
res.wrappers = wrappers.fork(res); res.wrappers = wrappers.fork(res);
res.global = global; res.global = global;
@ -80,7 +80,7 @@ public class Environment implements Extensions {
} }
public Environment(WrapperProvider nativeConverter, GlobalScope global) { public Environment(WrapperProvider nativeConverter, GlobalScope global) {
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this); if (nativeConverter == null) nativeConverter = new NativeWrapperProvider();
if (global == null) global = new GlobalScope(); if (global == null) global = new GlobalScope();
this.wrappers = nativeConverter; this.wrappers = nativeConverter;

View File

@ -53,6 +53,7 @@ public class SimpleDebugger implements Debugger {
); );
public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e<i;++e)t=t[n[e]];return t}"; public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e<i;++e)t=t[n[e]];return t}";
public static final String CHROME_GET_PROP_FUNC_2 = "function invokeGetter(getter) { return Reflect.apply(getter, this, []);}";
public static final String VSCODE_CALL = "function(t){return t.call(this)\n}"; public static final String VSCODE_CALL = "function(t){return t.call(this)\n}";
public static final String VSCODE_AUTOCOMPLETE = "function(t,e,r){let n=r?\"variable\":\"property\",i=(l,p,f)=>{if(p!==\"function\")return n;if(l===\"constructor\")return\"class\";let m=String(f);return m.startsWith(\"class \")||m.includes(\"[native code]\")&&/^[A-Z]/.test(l)?\"class\":r?\"function\":\"method\"\n},o=l=>{switch(typeof l){case\"number\":case\"boolean\":return`${l}`;case\"object\":return l===null?\"null\":l.constructor.name||\"object\";case\"function\":return`fn(${new Array(l.length).fill(\"?\").join(\", \")})`;default:return typeof l}},s=[],a=new Set,u=\"~\",c=t===void 0?this:t;for(;c!=null;c=c.__proto__){u+=\"~\";let l=Object.getOwnPropertyNames(c).filter(p=>p.startsWith(e)&&!p.match(/^\\d+$/));for(let p of l){if(a.has(p))continue;a.add(p);let f=Object.getOwnPropertyDescriptor(c,p),m=n,h;try{let H=c[p];m=i(p,typeof f?.value,H),h=o(H)}catch{}s.push({label:p,sortText:u+p.replace(/^_+/,H=>\"{\".repeat(H.length)),type:m,detail:h})}r=!1}return{result:s,isArray:this instanceof Array}}"; public static final String VSCODE_AUTOCOMPLETE = "function(t,e,r){let n=r?\"variable\":\"property\",i=(l,p,f)=>{if(p!==\"function\")return n;if(l===\"constructor\")return\"class\";let m=String(f);return m.startsWith(\"class \")||m.includes(\"[native code]\")&&/^[A-Z]/.test(l)?\"class\":r?\"function\":\"method\"\n},o=l=>{switch(typeof l){case\"number\":case\"boolean\":return`${l}`;case\"object\":return l===null?\"null\":l.constructor.name||\"object\";case\"function\":return`fn(${new Array(l.length).fill(\"?\").join(\", \")})`;default:return typeof l}},s=[],a=new Set,u=\"~\",c=t===void 0?this:t;for(;c!=null;c=c.__proto__){u+=\"~\";let l=Object.getOwnPropertyNames(c).filter(p=>p.startsWith(e)&&!p.match(/^\\d+$/));for(let p of l){if(a.has(p))continue;a.add(p);let f=Object.getOwnPropertyDescriptor(c,p),m=n,h;try{let H=c[p];m=i(p,typeof f?.value,H),h=o(H)}catch{}s.push({label:p,sortText:u+p.replace(/^_+/,H=>\"{\".repeat(H.length)),type:m,detail:h})}r=!1}return{result:s,isArray:this instanceof Array}}";
@ -161,29 +162,29 @@ public class SimpleDebugger implements Debugger {
.add(new JSONMap() .add(new JSONMap()
.set("type", "local") .set("type", "local")
.set("name", "Local Scope") .set("name", "Local Scope")
.set("object", serializeObj(frame.ctx, local)) .set("object", serializeObj(frame.ctx.environment, local))
) )
.add(new JSONMap() .add(new JSONMap()
.set("type", "closure") .set("type", "closure")
.set("name", "Closure") .set("name", "Closure")
.set("object", serializeObj(frame.ctx, capture)) .set("object", serializeObj(frame.ctx.environment, capture))
) )
.add(new JSONMap() .add(new JSONMap()
.set("type", "global") .set("type", "global")
.set("name", "Global Scope") .set("name", "Global Scope")
.set("object", serializeObj(frame.ctx, global)) .set("object", serializeObj(frame.ctx.environment, global))
) )
.add(new JSONMap() .add(new JSONMap()
.set("type", "other") .set("type", "other")
.set("name", "Value Stack") .set("name", "Value Stack")
.set("object", serializeObj(frame.ctx, valstack)) .set("object", serializeObj(frame.ctx.environment, valstack))
) )
); );
} }
} }
private class ObjRef { private class ObjRef {
public final ObjectValue obj; public final ObjectValue obj;
public final Context ctx; public final Environment env;
public final HashSet<String> heldGroups = new HashSet<>(); public final HashSet<String> heldGroups = new HashSet<>();
public boolean held = true; public boolean held = true;
@ -191,19 +192,19 @@ public class SimpleDebugger implements Debugger {
return !held && heldGroups.size() == 0; return !held && heldGroups.size() == 0;
} }
public ObjRef(Context ctx, ObjectValue obj) { public ObjRef(Environment env, ObjectValue obj) {
this.ctx = ctx.environment.context(); this.env = env;
this.obj = obj; this.obj = obj;
} }
} }
private static class RunResult { private static class RunResult {
public final Context ctx; public final Environment ctx;
public final Object result; public final Object result;
public final EngineException error; public final EngineException error;
public RunResult(Context ctx, Object result, EngineException error) { public RunResult(Environment env, Object result, EngineException error) {
this.ctx = ctx; this.ctx = env;
this.result = result; this.result = result;
this.error = error; this.error = error;
} }
@ -337,21 +338,10 @@ public class SimpleDebugger implements Debugger {
.set("columnNumber", loc.start() - 1); .set("columnNumber", loc.start() - 1);
} }
private Integer objectId(Context ctx, ObjectValue obj) { private JSONMap serializeObj(Environment env, Object val, boolean byValue) {
if (objectToId.containsKey(obj)) return objectToId.get(obj);
else {
int id = nextId();
var ref = new ObjRef(ctx, obj);
objectToId.put(obj, id);
idToObject.put(id, ref);
return id;
}
}
private JSONMap serializeObj(Context ctx, Object val, boolean byValue) {
val = Values.normalize(null, val); val = Values.normalize(null, val);
var newEnv = ctx.environment.child(); env = sanitizeEnvironment(env);
newEnv.add(DebugContext.IGNORE); var ctx = env.context();
ctx = newEnv.context();
if (val == Values.NULL) { if (val == Values.NULL) {
return new JSONMap() return new JSONMap()
@ -363,7 +353,16 @@ public class SimpleDebugger implements Debugger {
if (val instanceof ObjectValue) { if (val instanceof ObjectValue) {
var obj = (ObjectValue)val; var obj = (ObjectValue)val;
var id = objectId(ctx, obj); int id;
if (objectToId.containsKey(obj)) id = objectToId.get(obj);
else {
id = nextId();
var ref = new ObjRef(env, obj);
objectToId.put(obj, id);
idToObject.put(id, ref);
}
var type = "object"; var type = "object";
String subtype = null; String subtype = null;
String className = null; String className = null;
@ -392,7 +391,7 @@ public class SimpleDebugger implements Debugger {
try { try {
defaultToString = defaultToString =
Values.getMember(ctx, obj, "toString") == Values.getMember(ctx, obj, "toString") ==
Values.getMember(ctx, ctx.get(Environment.OBJECT_PROTO), "toString"); Values.getMember(ctx, env.get(Environment.OBJECT_PROTO), "toString");
} }
catch (Exception e) { } catch (Exception e) { }
@ -401,7 +400,7 @@ public class SimpleDebugger implements Debugger {
} }
if (byValue) try { res.put("value", JSON.fromJs(ctx, obj)); } if (byValue) try { res.put("value", JSON.fromJs(env.context(), obj)); }
catch (Exception e) { } catch (Exception e) { }
return res; return res;
@ -427,8 +426,8 @@ public class SimpleDebugger implements Debugger {
throw new IllegalArgumentException("Unexpected JS object."); throw new IllegalArgumentException("Unexpected JS object.");
} }
private JSONMap serializeObj(Context ctx, Object val) { private JSONMap serializeObj(Environment env, Object val) {
return serializeObj(ctx, val, false); return serializeObj(env, val, false);
} }
private void addObjectGroup(String name, Object val) { private void addObjectGroup(String name, Object val) {
if (val instanceof ObjectValue) { if (val instanceof ObjectValue) {
@ -467,11 +466,11 @@ public class SimpleDebugger implements Debugger {
else return JSON.toJs(res); else return JSON.toJs(res);
} }
private JSONMap serializeException(Context ctx, EngineException err) { private JSONMap serializeException(Environment env, EngineException err) {
String text = null; String text = null;
try { try {
text = Values.toString(ctx, err.value); text = Values.toString(env.context(), err.value);
} }
catch (EngineException e) { catch (EngineException e) {
text = "[error while stringifying]"; text = "[error while stringifying]";
@ -479,7 +478,7 @@ public class SimpleDebugger implements Debugger {
var res = new JSONMap() var res = new JSONMap()
.set("exceptionId", nextId()) .set("exceptionId", nextId())
.set("exception", serializeObj(ctx, err.value)) .set("exception", serializeObj(env, err.value))
.set("text", text); .set("text", text);
return res; return res;
@ -540,32 +539,42 @@ public class SimpleDebugger implements Debugger {
} }
} }
private Environment sanitizeEnvironment(Environment env) {
var res = env.child();
res.remove(EventLoop.KEY);
res.remove(DebugContext.KEY);
res.add(DebugContext.IGNORE);
return res;
}
private RunResult run(DebugFrame codeFrame, String code) { private RunResult run(DebugFrame codeFrame, String code) {
if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!"));
var engine = new Engine(); var engine = new Engine();
var env = codeFrame.frame.function.environment.copy(); var env = codeFrame.frame.ctx.environment.copy();
env.global = new GlobalScope(codeFrame.local); env.global = new GlobalScope(codeFrame.local);
env.remove(EventLoop.KEY); env.remove(EventLoop.KEY);
env.remove(DebugContext.KEY); env.remove(DebugContext.KEY);
env.add(EventLoop.KEY, engine); env.add(EventLoop.KEY, engine);
var ctx = new Context(env); var awaiter = engine.pushMsg(false, env, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
var awaiter = engine.pushMsg(false, ctx.environment, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
try { try {
engine.run(true); engine.run(true);
return new RunResult(ctx, awaiter.await(), null); return new RunResult(env, awaiter.await(), null);
} }
catch (EngineException e) { return new RunResult(ctx, null, e); } catch (EngineException e) { return new RunResult(env, null, e); }
catch (SyntaxException e) { return new RunResult(ctx, null, EngineException.ofSyntax(e.toString())); } catch (SyntaxException e) { return new RunResult(env, null, EngineException.ofSyntax(e.toString())); }
} }
private ObjectValue vscodeAutoSuggest(Context ctx, Object target, String query, boolean variable) { private ObjectValue vscodeAutoSuggest(Environment env, Object target, String query, boolean variable) {
var res = new ArrayValue(); var res = new ArrayValue();
var passed = new HashSet<String>(); var passed = new HashSet<String>();
var tildas = "~"; var tildas = "~";
if (target == null) target = ctx.environment.global; var ctx = env.context();
if (target == null) target = env.global;
for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) { for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) {
for (var el : Values.getMembers(ctx, proto, true, true)) { for (var el : Values.getMembers(ctx, proto, true, true)) {
@ -620,7 +629,7 @@ public class SimpleDebugger implements Debugger {
desc.defineProperty(ctx, "type", Values.type(val)); desc.defineProperty(ctx, "type", Values.type(val));
break; break;
} }
res.set(ctx, res.size(), desc); res.set(ctx, res.size(), desc);
} }
@ -648,6 +657,10 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond()); ws.send(msg.respond());
} }
@Override public synchronized void close() { @Override public synchronized void close() {
if (state != State.RESUMED) {
resume(State.RESUMED);
}
enabled = false; enabled = false;
execptionType = CatchType.NONE; execptionType = CatchType.NONE;
state = State.RESUMED; state = State.RESUMED;
@ -838,46 +851,54 @@ public class SimpleDebugger implements Debugger {
@Override public synchronized void getProperties(V8Message msg) throws IOException { @Override public synchronized void getProperties(V8Message msg) throws IOException {
var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var obj = ref.obj; var obj = ref.obj;
var env = ref.env;
var ctx = env.context();
var res = new JSONList(); var res = new JSONList();
var ctx = ref.ctx; var own = true;
if (obj != emptyObject && obj != null) { if (obj != emptyObject && obj != null) {
for (var key : obj.keys(true)) { while (obj != null) {
var propDesc = new JSONMap(); for (var key : obj.keys(true)) {
var propDesc = new JSONMap();
if (obj.properties.containsKey(key)) { if (obj.properties.containsKey(key)) {
var prop = obj.properties.get(key); var prop = obj.properties.get(key);
propDesc.set("name", Values.toString(ctx, key)); propDesc.set("name", Values.toString(ctx, key));
if (prop.getter != null) propDesc.set("get", serializeObj(ctx, prop.getter)); if (prop.getter != null) propDesc.set("get", serializeObj(env, prop.getter));
if (prop.setter != null) propDesc.set("set", serializeObj(ctx, prop.setter)); if (prop.setter != null) propDesc.set("set", serializeObj(env, prop.setter));
propDesc.set("enumerable", obj.memberEnumerable(key)); propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key)); propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", true); propDesc.set("isOwn", true);
res.add(propDesc); res.add(propDesc);
}
else {
propDesc.set("name", Values.toString(ctx, key));
propDesc.set("value", serializeObj(env, Values.getMember(ctx, obj, key)));
propDesc.set("writable", obj.memberWritable(key));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
propDesc.set("isOwn", own);
res.add(propDesc);
}
} }
else {
propDesc.set("name", Values.toString(ctx, key)); var proto = Values.getPrototype(ctx, obj);
propDesc.set("value", serializeObj(ctx, Values.getMember(ctx, obj, key)));
propDesc.set("writable", obj.memberWritable(key)); if (own) {
propDesc.set("enumerable", obj.memberEnumerable(key)); var protoDesc = new JSONMap();
propDesc.set("configurable", obj.memberConfigurable(key)); protoDesc.set("name", "__proto__");
propDesc.set("isOwn", true); protoDesc.set("value", serializeObj(env, proto == null ? Values.NULL : proto));
res.add(propDesc); protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
protoDesc.set("isOwn", own);
res.add(protoDesc);
} }
obj = proto;
own = false;
} }
var proto = Values.getPrototype(ctx, obj);
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
protoDesc.set("value", serializeObj(ctx, proto == null ? Values.NULL : proto));
protoDesc.set("writable", true);
protoDesc.set("enumerable", false);
protoDesc.set("configurable", false);
protoDesc.set("isOwn", true);
res.add(protoDesc);
} }
ws.send(msg.respond(new JSONMap().set("result", res))); ws.send(msg.respond(new JSONMap().set("result", res)));
@ -894,7 +915,8 @@ public class SimpleDebugger implements Debugger {
var thisArgRef = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); var thisArgRef = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var thisArg = thisArgRef.obj; var thisArg = thisArgRef.obj;
var ctx = thisArgRef.ctx; var env = thisArgRef.env;
var ctx = env.context();
while (true) { while (true) {
var start = src.lastIndexOf("//# sourceURL="); var start = src.lastIndexOf("//# sourceURL=");
@ -912,22 +934,25 @@ public class SimpleDebugger implements Debugger {
res = thisArg; res = thisArg;
for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(ctx, res, JSON.toJs(el)); for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(ctx, res, JSON.toJs(el));
} }
else if (compare(src, CHROME_GET_PROP_FUNC_2)) {
res = Values.call(ctx, args.get(0), thisArg);
}
else if (compare(src, VSCODE_CALL)) { else if (compare(src, VSCODE_CALL)) {
var func = (FunctionValue)(args.size() < 1 ? null : args.get(0)); var func = (FunctionValue)(args.size() < 1 ? null : args.get(0));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, func.call(ctx, thisArg))))); ws.send(msg.respond(new JSONMap().set("result", serializeObj(env, func.call(ctx, thisArg)))));
} }
else if (compare(src, VSCODE_AUTOCOMPLETE)) { else if (compare(src, VSCODE_AUTOCOMPLETE)) {
var target = args.get(0); var target = args.get(0);
if (target == null) target = thisArg; if (target == null) target = thisArg;
res = vscodeAutoSuggest(ctx, target, Values.toString(ctx, args.get(1)), Values.toBoolean(args.get(2))); res = vscodeAutoSuggest(env, target, Values.toString(ctx, args.get(1)), Values.toBoolean(args.get(2)));
} }
else { else {
ws.send(new V8Error("Please use well-known functions with callFunctionOn")); ws.send(new V8Error("Please use well-known functions with callFunctionOn"));
return; return;
} }
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res, byValue)))); ws.send(msg.respond(new JSONMap().set("result", serializeObj(env, res, byValue))));
} }
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); } catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(env, e)))); }
} }
@Override public synchronized void runtimeEnable(V8Message msg) throws IOException { @Override public synchronized void runtimeEnable(V8Message msg) throws IOException {

View File

@ -1,7 +1,6 @@
package me.topchetoeu.jscript.utils.interop; package me.topchetoeu.jscript.utils.interop;
public enum ExposeType { public enum ExposeType {
INIT,
METHOD, METHOD,
GETTER, GETTER,
SETTER, SETTER,

View File

@ -25,8 +25,8 @@ public class NativeWrapperProvider implements WrapperProvider {
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>(); private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>(); private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>(); private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>();
private final HashMap<Class<?>, Class<?>> proxies = new HashMap<>();
private final HashSet<Class<?>> ignore = new HashSet<>(); private final HashSet<Class<?>> ignore = new HashSet<>();
private final Environment env;
private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) { private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) {
try { try {
@ -107,7 +107,7 @@ public class NativeWrapperProvider implements WrapperProvider {
else return name; else return name;
} }
private static boolean apply(ObjectValue obj, Environment env, ExposeTarget target, Class<?> clazz) { private static boolean apply(ObjectValue obj, ExposeTarget target, Class<?> clazz) {
var getters = new HashMap<Object, FunctionValue>(); var getters = new HashMap<Object, FunctionValue>();
var setters = new HashMap<Object, FunctionValue>(); var setters = new HashMap<Object, FunctionValue>();
var props = new HashSet<Object>(); var props = new HashSet<Object>();
@ -125,13 +125,6 @@ public class NativeWrapperProvider implements WrapperProvider {
any = true; any = true;
switch (annotation.type()) { switch (annotation.type()) {
case INIT:
checkSignature(method, true,
target == ExposeTarget.CONSTRUCTOR ? FunctionValue.class : ObjectValue.class,
Environment.class
);
call(null, null, method, obj, null, env);
break;
case METHOD: case METHOD:
if (props.contains(key) || nonProps.contains(key)) repeat = true; if (props.contains(key) || nonProps.contains(key)) repeat = true;
else { else {
@ -175,8 +168,13 @@ public class NativeWrapperProvider implements WrapperProvider {
if (props.contains(key) || nonProps.contains(key)) repeat = true; if (props.contains(key) || nonProps.contains(key)) repeat = true;
else { else {
checkSignature(method, true, Environment.class); checkSignature(method, true);
obj.defineProperty(null, key, call(new Context(env), name, method, null, env), true, true, false); try {
obj.defineProperty(null, key, method.invoke(null), true, true, false);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
nonProps.add(key); nonProps.add(key);
} }
@ -201,7 +199,7 @@ public class NativeWrapperProvider implements WrapperProvider {
else { else {
try { try {
if (Modifier.isStatic(field.getModifiers())) { if (Modifier.isStatic(field.getModifiers())) {
obj.defineProperty(null, key, Values.normalize(new Context(env), field.get(null)), true, true, false); obj.defineProperty(null, key, Values.normalize(null, field.get(null)), true, true, false);
} }
else { else {
obj.defineProperty( obj.defineProperty(
@ -236,7 +234,7 @@ public class NativeWrapperProvider implements WrapperProvider {
return any; return any;
} }
private static Method getConstructor(Environment env, Class<?> clazz) { private static Method getConstructor(Class<?> clazz) {
for (var method : clazz.getDeclaredMethods()) { for (var method : clazz.getDeclaredMethods()) {
if (!method.isAnnotationPresent(ExposeConstructor.class)) continue; if (!method.isAnnotationPresent(ExposeConstructor.class)) continue;
checkSignature(method, true, Arguments.class); checkSignature(method, true, Arguments.class);
@ -252,10 +250,10 @@ public class NativeWrapperProvider implements WrapperProvider {
* All accessors and methods will expect the this argument to be a native wrapper of the given class type. * 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 * @param clazz The class for which a prototype should be generated
*/ */
public static ObjectValue makeProto(Environment env, Class<?> clazz) { public static ObjectValue makeProto(Class<?> clazz) {
var res = new ObjectValue(); var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz)); res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
if (!apply(res, env, ExposeTarget.PROTOTYPE, clazz)) return null; if (!apply(res, ExposeTarget.PROTOTYPE, clazz)) return null;
return res; return res;
} }
/** /**
@ -264,14 +262,14 @@ public class NativeWrapperProvider implements WrapperProvider {
* When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible. * 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 * @param clazz The class for which a constructor should be generated
*/ */
public static FunctionValue makeConstructor(Environment ctx, Class<?> clazz) { public static FunctionValue makeConstructor(Class<?> clazz) {
var constr = getConstructor(ctx, clazz); var constr = getConstructor(clazz);
FunctionValue res = constr == null ? FunctionValue res = constr == null ?
new NativeFunction(getName(clazz), args -> { throw EngineException.ofError("This constructor is not invokable."); }) : new NativeFunction(getName(clazz), args -> { throw EngineException.ofError("This constructor is not invokable."); }) :
create(getName(clazz), constr); create(getName(clazz), constr);
apply(res, ctx, ExposeTarget.CONSTRUCTOR, clazz); apply(res, ExposeTarget.CONSTRUCTOR, clazz);
return res; return res;
} }
@ -281,10 +279,10 @@ public class NativeWrapperProvider implements WrapperProvider {
* This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead. * 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 * @param clazz The class for which a constructor should be generated
*/ */
public static ObjectValue makeNamespace(Environment ctx, Class<?> clazz) { public static ObjectValue makeNamespace(Class<?> clazz) {
var res = new ObjectValue(); var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz)); res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
if (!apply(res, ctx, ExposeTarget.NAMESPACE, clazz)) return null; if (!apply(res, ExposeTarget.NAMESPACE, clazz)) return null;
return res; return res;
} }
@ -320,8 +318,8 @@ public class NativeWrapperProvider implements WrapperProvider {
clazz.isSynthetic() clazz.isSynthetic()
) return; ) return;
if (constr == null) constr = makeConstructor(env, clazz); if (constr == null) constr = makeConstructor(clazz);
if (proto == null) proto = makeProto(env, clazz); if (proto == null) proto = makeProto(clazz);
if (constr == null || proto == null) return; if (constr == null || proto == null) return;
@ -344,7 +342,7 @@ public class NativeWrapperProvider implements WrapperProvider {
return null; return null;
} }
public ObjectValue getNamespace(Class<?> clazz) { public ObjectValue getNamespace(Class<?> clazz) {
if (!namespaces.containsKey(clazz)) namespaces.put(clazz, makeNamespace(env, clazz)); if (!namespaces.containsKey(clazz)) namespaces.put(clazz, makeNamespace(clazz));
while (clazz != null) { while (clazz != null) {
var res = namespaces.get(clazz); var res = namespaces.get(clazz);
if (res != null) return res; if (res != null) return res;
@ -363,52 +361,34 @@ public class NativeWrapperProvider implements WrapperProvider {
} }
@Override public WrapperProvider fork(Environment env) { @Override public WrapperProvider fork(Environment env) {
return new NativeWrapperProvider(env); var res = new NativeWrapperProvider();
for (var pair : proxies.entrySet()) {
res.set(pair.getKey(), pair.getValue());
}
return this;
} }
public void set(Class<?> clazz, Class<?> wrapper) { public void set(Class<?> clazz, Class<?> wrapper) {
if (wrapper == null) set(clazz, null, null); if (clazz == null) return;
else set(clazz, makeProto(env, wrapper), makeConstructor(env, wrapper)); if (wrapper == null) wrapper = clazz;
}
public void set(Class<?> clazz, ObjectValue proto, FunctionValue constructor) {
if (proto != null && constructor != null) {
prototypes.put(clazz, proto);
constructors.put(clazz, constructor);
ignore.remove(clazz);
for (var el : prototypes.keySet()) { ignore.remove(clazz);
if (clazz.isAssignableFrom(el)) {
updateProtoChain(el, getProto(el), getConstr(el));
}
}
}
else {
prototypes.remove(clazz);
constructors.remove(clazz);
ignore.remove(clazz);
initType(clazz, constructor, proto); var proto = makeProto(wrapper);
} var constr = makeConstructor(wrapper);
}
private void initError() { prototypes.put(clazz, proto);
var proto = new ObjectValue(); constructors.put(clazz, constr);
proto.defineProperty(null, "message", new NativeFunction("message", args -> {
if (args.self instanceof Throwable) return ((Throwable)args.self).getMessage();
else return null;
}));
proto.defineProperty(null, "name", new NativeFunction("name", args -> getName(args.self.getClass())));
proto.defineProperty(null, "toString", new NativeFunction("toString", args -> args.self.toString()));
var constr = makeConstructor(null, Throwable.class);
proto.defineProperty(null, "constructor", constr, true, false, false); proto.defineProperty(null, "constructor", constr, true, false, false);
constr.defineProperty(null, "prototype", proto, true, false, false); constr.defineProperty(null, "prototype", proto, true, false, false);
set(Throwable.class, proto, constr); for (var el : prototypes.keySet()) {
if (clazz.isAssignableFrom(el)) {
updateProtoChain(el, getProto(el), getConstr(el));
}
}
} }
public NativeWrapperProvider(Environment env) { public NativeWrapperProvider() { }
this.env = env;
initError();
}
} }