Compare commits

..

12 Commits

7 changed files with 130 additions and 57 deletions

View File

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

View File

@@ -104,7 +104,9 @@ public class FunctionMap {
var res = new ArrayList<Location>(candidates.size()); var res = new ArrayList<Location>(candidates.size());
for (var candidate : candidates.entrySet()) { for (var candidate : candidates.entrySet()) {
res.add(candidate.getValue().ceiling(new Location(line, column, candidate.getKey()))); var val = correctBreakpoint(new Location(line, column, candidate.getKey()));
if (val == null) continue;
res.add(val);
} }
return res; return res;

View File

@@ -32,10 +32,17 @@ public class DebugContext {
if (sources != null) { if (sources != null) {
for (var source : sources.entrySet()) debugger.onSourceLoad(source.getKey(), source.getValue()); for (var source : sources.entrySet()) debugger.onSourceLoad(source.getKey(), source.getValue());
} }
if (maps != null) {
for (var map : maps.entrySet()) debugger.onFunctionLoad(map.getKey(), map.getValue());
}
this.debugger = debugger; this.debugger = debugger;
return true; return true;
} }
public boolean detachDebugger(DebugHandler debugger) {
if (this.debugger != debugger) return false;
return detachDebugger();
}
public boolean detachDebugger() { public boolean detachDebugger() {
this.debugger = null; this.debugger = null;
return true; return true;

View File

@@ -1,15 +1,21 @@
package me.topchetoeu.jscript.runtime.values; package me.topchetoeu.jscript.runtime.values;
import java.util.WeakHashMap;
import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
public class NativeWrapper extends ObjectValue { public class NativeWrapper extends ObjectValue {
private static final Key<WeakHashMap<Object, NativeWrapper>> WRAPPERS = new Key<>();
private static final Object NATIVE_PROTO = new Object(); private static final Object NATIVE_PROTO = new Object();
public final Object wrapped; public final Object wrapped;
@Override @Override
public ObjectValue getPrototype(Context ctx) { public ObjectValue getPrototype(Context ctx) {
if (ctx.environment != null && prototype == NATIVE_PROTO) { if (ctx.environment != null && prototype == NATIVE_PROTO) {
var res = ctx.environment.wrappers.getProto(wrapped.getClass()); var clazz = wrapped.getClass();
var res = ctx.environment.wrappers.getProto(clazz);
if (res != null) return res; if (res != null) return res;
} }
return super.getPrototype(ctx); return super.getPrototype(ctx);
@@ -28,8 +34,20 @@ public class NativeWrapper extends ObjectValue {
return wrapped.hashCode(); return wrapped.hashCode();
} }
public NativeWrapper(Object wrapped) { private NativeWrapper(Object wrapped) {
this.wrapped = wrapped; this.wrapped = wrapped;
prototype = NATIVE_PROTO; prototype = NATIVE_PROTO;
} }
public static NativeWrapper of(Extensions exts, Object wrapped) {
var wrappers = exts == null ? null : exts.get(WRAPPERS);
if (wrappers == null) return new NativeWrapper(wrapped);
if (wrappers.containsKey(wrapped)) return wrappers.get(wrapped);
var res = new NativeWrapper(wrapped);
wrappers.put(wrapped, res);
return res;
}
} }

View File

@@ -58,9 +58,8 @@ public class Values {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T wrapper(Object val, Class<T> clazz) { public static <T> T wrapper(Object val, Class<T> clazz) {
if (!isWrapper(val)) val = new NativeWrapper(val); if (isWrapper(val)) val = ((NativeWrapper)val).wrapped;
var res = (NativeWrapper)val; if (val != null && clazz.isInstance(val)) return (T)val;
if (res != null && clazz.isInstance(res.wrapped)) return (T)res.wrapped;
else return null; else return null;
} }
@@ -471,7 +470,7 @@ public class Values {
else return ctx.environment.wrappers.getConstr((Class<?>)val); else return ctx.environment.wrappers.getConstr((Class<?>)val);
} }
return new NativeWrapper(val); return NativeWrapper.of(ctx, val);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -536,7 +535,7 @@ public class Values {
if (obj == null) return null; if (obj == null) return null;
if (clazz.isInstance(obj)) return (T)obj; if (clazz.isInstance(obj)) return (T)obj;
if (clazz.isAssignableFrom(NativeWrapper.class)) { if (clazz.isAssignableFrom(NativeWrapper.class)) {
return (T)new NativeWrapper(obj); return (T)NativeWrapper.of(ctx, obj);
} }
throw new ConvertException(type(obj), clazz.getSimpleName()); throw new ConvertException(type(obj), clazz.getSimpleName());

View File

@@ -30,6 +30,7 @@ import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.Frame;
import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.FunctionValue;
@@ -41,7 +42,9 @@ import me.topchetoeu.jscript.runtime.values.Values;
public class SimpleDebugger implements Debugger { public class SimpleDebugger implements Debugger {
public static final Set<String> VSCODE_EMPTY = Set.of( public static final Set<String> VSCODE_EMPTY = Set.of(
"function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<<default preview>>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<<indescribable>>${JSON.stringify([String(r),\"object\"])}`}if(typeof this==\"object\"&&this){let r;for(let i of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])try{r=this[i]();break}catch{}if(!r&&!String(this.toString).includes(\"[native code]\")&&(r=String(this)),r&&!r.startsWith(\"[object \"))return r.length>=t?r.slice(0,t)+\"\\u2026\":r}\n ;\n\n}", "function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<<default preview>>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<<indescribable>>${JSON.stringify([String(r),\"object\"])}`}if(typeof this==\"object\"&&this){let r;for(let i of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])try{r=this[i]();break}catch{}if(!r&&!String(this.toString).includes(\"[native code]\")&&(r=String(this)),r&&!r.startsWith(\"[object \"))return r.length>=t?r.slice(0,t)+\"\\u2026\":r}\n ;\n\n}",
"function(...runtimeArgs){\n let r = 1024; let e = null;\n if(e)try{let t=\"<<default preview>>\",n=e.call(this,t);if(n!==t)return String(n)}catch(t){return`<<indescribable>>${JSON.stringify([String(t),\"object\"])}`}if(typeof this==\"object\"&&this){let t;for(let n of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])if(typeof this[n]==\"function\")try{t=this[n]();break}catch{}if(!t&&!String(this.toString).includes(\"[native code]\")&&(t=String(this)),t&&!t.startsWith(\"[object\"))return t.length>=r?t.slice(0,r)+\"\\u2026\":t};}",
"function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<<default preview>>\";if(typeof this!=\"object\"||!this)return r;for(let[n,s]of Object.entries(this)){if(e)try{let o=e.call(s,i);if(o!==i){r[n]=String(o);continue}}catch(o){r[n]=`<<indescribable>>${JSON.stringify([String(o),n])}`;continue}if(typeof s==\"object\"&&s){let o;for(let a of runtimeArgs[0])try{o=s[a]();break}catch{}!o&&!String(s.toString).includes(\"[native code]\")&&(o=String(s)),o&&!o.startsWith(\"[object \")&&(r[n]=o.length>=t?o.slice(0,t)+\"\\u2026\":o)}}return r\n ;\n\n}", "function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<<default preview>>\";if(typeof this!=\"object\"||!this)return r;for(let[n,s]of Object.entries(this)){if(e)try{let o=e.call(s,i);if(o!==i){r[n]=String(o);continue}}catch(o){r[n]=`<<indescribable>>${JSON.stringify([String(o),n])}`;continue}if(typeof s==\"object\"&&s){let o;for(let a of runtimeArgs[0])try{o=s[a]();break}catch{}!o&&!String(s.toString).includes(\"[native code]\")&&(o=String(s)),o&&!o.startsWith(\"[object \")&&(r[n]=o.length>=t?o.slice(0,t)+\"\\u2026\":o)}}return r\n ;\n\n}",
"function(...runtimeArgs){\n let r = 1024; let e = null;\n let t={},n=\"<<default preview>>\";if(typeof this!=\"object\"||!this)return t;for(let[i,o]of Object.entries(this)){if(e)try{let s=e.call(o,n);if(s!==n){t[i]=String(s);continue}}catch(s){t[i]=`<<indescribable>>${JSON.stringify([String(s),i])}`;continue}if(typeof o==\"object\"&&o){let s;for(let a of runtimeArgs[0])if(typeof o[a]==\"function\")try{s=o[a]();break}catch{}!s&&!String(o.toString).includes(\"[native code]\")&&(s=String(o)),s&&!s.startsWith(\"[object \")&&(t[i]=s.length>=r?s.slice(0,r)+\"\\u2026\":s)}}return t\n ;\n\n}",
"function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r<e.length;++r){let i=e[r],n=i>>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}", "function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r<e.length;++r){let i=e[r],n=i>>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}",
"function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}" "function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}"
); );
@@ -83,7 +86,9 @@ public class SimpleDebugger implements Debugger {
public final String condition; public final String condition;
public final Pattern pattern; public final Pattern pattern;
public final int line, start; public final int line, start;
public final WeakHashMap<FunctionBody, Set<Location>> resolvedLocations = new WeakHashMap<>(); public final long locNum;
public final HashMap<Filename, Location> resolvedLocations = new HashMap<>();
public final HashMap<Filename, Long> resolvedDistances = new HashMap<>();
public Breakpoint(int id, Pattern pattern, int line, int start, String condition) { public Breakpoint(int id, Pattern pattern, int line, int start, String condition) {
this.id = id; this.id = id;
@@ -91,18 +96,28 @@ public class SimpleDebugger implements Debugger {
this.pattern = pattern; this.pattern = pattern;
this.line = line; this.line = line;
this.start = start; this.start = start;
this.locNum = start | ((long)line << 32);
if (condition != null && condition.trim().equals("")) condition = null; if (condition != null && condition.trim().equals("")) condition = null;
} }
// TODO: Figure out how to unload a breakpoint // TODO: Figure out how to unload a breakpoint
// TODO: Do location resolution with function boundaries
public void addFunc(FunctionBody body, FunctionMap map) { public void addFunc(FunctionBody body, FunctionMap map) {
try { try {
for (var loc : map.correctBreakpoint(pattern, line, start)) { for (var loc : map.correctBreakpoint(pattern, line, start)) {
if (!resolvedLocations.containsKey(body)) resolvedLocations.put(body, new HashSet<>()); var currNum = loc.start() + ((long)loc.line() << 32);
var set = resolvedLocations.get(body); long currDist = 0;
set.add(loc); if (currNum > locNum) currDist = currNum - locNum;
else currDist = locNum - currNum;
if ( currDist > resolvedDistances.getOrDefault(loc.filename(), Long.MAX_VALUE)) continue;
resolvedLocations.put(loc.filename(), loc);
resolvedDistances.put(loc.filename(), currDist);
}
for (var loc : resolvedLocations.values()) {
ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap() ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap()
.set("breakpointId", id) .set("breakpointId", id)
.set("location", serializeLocation(loc)) .set("location", serializeLocation(loc))
@@ -202,8 +217,9 @@ public class SimpleDebugger implements Debugger {
private ObjectValue emptyObject = new ObjectValue(); private ObjectValue emptyObject = new ObjectValue();
private WeakHashMap<DebugContext, DebugContext> contexts = new WeakHashMap<>();
private WeakHashMap<FunctionBody, FunctionMap> mappings = new WeakHashMap<>(); private WeakHashMap<FunctionBody, FunctionMap> mappings = new WeakHashMap<>();
private WeakHashMap<FunctionBody, HashMap<Location, Breakpoint>> bpLocs = new WeakHashMap<>(); private HashMap<Location, HashSet<Breakpoint>> bpLocs = new HashMap<>();
private HashMap<Integer, Breakpoint> idToBreakpoint = new HashMap<>(); private HashMap<Integer, Breakpoint> idToBreakpoint = new HashMap<>();
@@ -226,6 +242,8 @@ public class SimpleDebugger implements Debugger {
private int stepOutPtr = 0; private int stepOutPtr = 0;
private boolean compare(String src, String target) { private boolean compare(String src, String target) {
src = src.replaceAll("\\s", "");
target = target.replaceAll("\\s", "");
if (src.length() != target.length()) return false; if (src.length() != target.length()) return false;
var diff = 0; var diff = 0;
var all = 0; var all = 0;
@@ -291,13 +309,11 @@ public class SimpleDebugger implements Debugger {
bpLocs.clear(); bpLocs.clear();
for (var bp : idToBreakpoint.values()) { for (var bp : idToBreakpoint.values()) {
for (var el : bp.resolvedLocations.entrySet()) { for (var loc : bp.resolvedLocations.values()) {
if (!bpLocs.containsKey(el.getKey())) bpLocs.put(el.getKey(), new HashMap<>()); bpLocs.putIfAbsent(loc, new HashSet<>());
var map = bpLocs.get(el.getKey()); var set = bpLocs.get(loc);
for (var loc : el.getValue()) { set.add(bp);
map.put(loc, bp);
}
} }
} }
} }
@@ -333,10 +349,10 @@ public class SimpleDebugger implements Debugger {
} }
private JSONMap serializeObj(Context ctx, Object val, boolean byValue) { private JSONMap serializeObj(Context ctx, Object val, boolean byValue) {
val = Values.normalize(null, val); val = Values.normalize(null, val);
var newCtx = new Context(); var newEnv = new Environment();
newCtx.addAll(ctx); newEnv.addAll(ctx);
newCtx.add(DebugContext.IGNORE); newEnv.add(DebugContext.IGNORE);
ctx = newCtx; ctx = newEnv.context();
if (val == Values.NULL) { if (val == Values.NULL) {
return new JSONMap() return new JSONMap()
@@ -366,6 +382,7 @@ public class SimpleDebugger implements Debugger {
if (subtype != null) res.set("subtype", subtype); if (subtype != null) res.set("subtype", subtype);
if (className != null) { if (className != null) {
res.set("className", className); res.set("className", className);
res.set("description", className);
} }
if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")"); if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")");
@@ -381,7 +398,7 @@ public class SimpleDebugger implements Debugger {
catch (Exception e) { } catch (Exception e) { }
try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(ctx, obj) + " }")); } try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(ctx, obj) + " }")); }
catch (EngineException e) { res.set("description", className); } catch (EngineException e) { }
} }
@@ -537,10 +554,12 @@ public class SimpleDebugger implements Debugger {
var ctx = new Context(env); var ctx = new Context(env);
var awaiter = engine.pushMsg(false, ctx.environment, 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);
engine.run(true); try {
engine.run(true);
try { return new RunResult(ctx, awaiter.await(), null); } return new RunResult(ctx, awaiter.await(), null);
}
catch (EngineException e) { return new RunResult(ctx, null, e); } catch (EngineException e) { return new RunResult(ctx, null, e); }
catch (SyntaxException e) { return new RunResult(ctx, null, EngineException.ofSyntax(e.toString())); }
} }
private ObjectValue vscodeAutoSuggest(Context ctx, Object target, String query, boolean variable) { private ObjectValue vscodeAutoSuggest(Context ctx, Object target, String query, boolean variable) {
@@ -634,11 +653,10 @@ public class SimpleDebugger implements Debugger {
execptionType = CatchType.NONE; execptionType = CatchType.NONE;
state = State.RESUMED; state = State.RESUMED;
// idToBptCand.clear(); mappings.clear();
bpLocs.clear();
idToBreakpoint.clear(); idToBreakpoint.clear();
// locToBreakpoint.clear();
// tmpBreakpts.clear();
filenameToId.clear(); filenameToId.clear();
idToSource.clear(); idToSource.clear();
@@ -656,6 +674,9 @@ public class SimpleDebugger implements Debugger {
stepOutFrame = currFrame = null; stepOutFrame = currFrame = null;
stepOutPtr = 0; stepOutPtr = 0;
for (var ctx : contexts.keySet()) ctx.detachDebugger(this);
contexts.clear();
updateNotifier.next(); updateNotifier.next();
} }
@@ -709,16 +730,15 @@ public class SimpleDebugger implements Debugger {
var bpt = new Breakpoint(nextId(), regex, line, col, cond); var bpt = new Breakpoint(nextId(), regex, line, col, cond);
idToBreakpoint.put(bpt.id, bpt); idToBreakpoint.put(bpt.id, bpt);
var locs = new JSONList();
for (var el : mappings.entrySet()) { for (var el : mappings.entrySet()) {
bpt.addFunc(el.getKey(), el.getValue()); bpt.addFunc(el.getKey(), el.getValue());
} }
for (var el : bpt.resolvedLocations.values()) { var locs = new JSONList();
for (var loc : el) {
locs.add(serializeLocation(loc)); for (var loc : bpt.resolvedLocations.values()) {
} locs.add(serializeLocation(loc));
} }
ws.send(msg.respond(new JSONMap() ws.send(msg.respond(new JSONMap()
@@ -730,6 +750,7 @@ public class SimpleDebugger implements Debugger {
var id = Integer.parseInt(msg.params.string("breakpointId")); var id = Integer.parseInt(msg.params.string("breakpointId"));
idToBreakpoint.remove(id); idToBreakpoint.remove(id);
updateBreakpoints();
ws.send(msg.respond()); ws.send(msg.respond());
} }
@Override public synchronized void continueToLocation(V8Message msg) throws IOException { @Override public synchronized void continueToLocation(V8Message msg) throws IOException {
@@ -958,10 +979,11 @@ public class SimpleDebugger implements Debugger {
) { ) {
pauseDebug(ctx, null); pauseDebug(ctx, null);
} }
else if (isBreakpointable && bpLocs.getOrDefault(cf.function.body, new HashMap<>()).containsKey(loc)) { else if (isBreakpointable && bpLocs.containsKey(loc)) {
var bp = bpLocs.get(cf.function.body).get(loc); for (var bp : bpLocs.get(loc)) {
var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result); var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result);
if (ok) pauseDebug(ctx, bp); if (ok) pauseDebug(ctx, bp);
}
} }
// else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null); // else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null);
else if (isBreakpointable && pendingPause) { else if (isBreakpointable && pendingPause) {
@@ -1033,6 +1055,7 @@ public class SimpleDebugger implements Debugger {
public SimpleDebugger attach(DebugContext ctx) { public SimpleDebugger attach(DebugContext ctx) {
ctx.attachDebugger(this); ctx.attachDebugger(this);
contexts.put(ctx, ctx);
return this; return this;
} }

View File

@@ -288,8 +288,23 @@ public class NativeWrapperProvider implements WrapperProvider {
return res; return res;
} }
private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
var parent = clazz;
while (true) {
parent = parent.getSuperclass();
if (parent == null) return;
var parentProto = getProto(parent);
var parentConstr = getConstr(parent);
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto);
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr);
}
}
private void initType(Class<?> clazz, FunctionValue constr, ObjectValue proto) { private void initType(Class<?> clazz, FunctionValue constr, ObjectValue proto) {
if (constr != null && proto != null) return; if (constr != null && proto != null || ignore.contains(clazz)) return;
// i vomit // i vomit
if ( if (
clazz == Object.class || clazz == Object.class ||
@@ -316,32 +331,35 @@ public class NativeWrapperProvider implements WrapperProvider {
prototypes.put(clazz, proto); prototypes.put(clazz, proto);
constructors.put(clazz, constr); constructors.put(clazz, constr);
var parent = clazz; updateProtoChain(clazz, proto, constr);
while (true) {
parent = parent.getSuperclass();
if (parent == null) return;
var parentProto = getProto(parent);
var parentConstr = getConstr(parent);
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto);
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr);
}
} }
public ObjectValue getProto(Class<?> clazz) { public ObjectValue getProto(Class<?> clazz) {
initType(clazz, constructors.get(clazz), prototypes.get(clazz)); initType(clazz, constructors.get(clazz), prototypes.get(clazz));
return prototypes.get(clazz); while (clazz != null) {
var res = prototypes.get(clazz);
if (res != null) return res;
clazz = clazz.getSuperclass();
}
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(env, clazz));
return namespaces.get(clazz); while (clazz != null) {
var res = namespaces.get(clazz);
if (res != null) return res;
clazz = clazz.getSuperclass();
}
return null;
} }
public FunctionValue getConstr(Class<?> clazz) { public FunctionValue getConstr(Class<?> clazz) {
initType(clazz, constructors.get(clazz), prototypes.get(clazz)); initType(clazz, constructors.get(clazz), prototypes.get(clazz));
return constructors.get(clazz); while (clazz != null) {
var res = constructors.get(clazz);
if (res != null) return res;
clazz = clazz.getSuperclass();
}
return null;
} }
@Override public WrapperProvider fork(Environment env) { @Override public WrapperProvider fork(Environment env) {
@@ -357,6 +375,12 @@ public class NativeWrapperProvider implements WrapperProvider {
prototypes.put(clazz, proto); prototypes.put(clazz, proto);
constructors.put(clazz, constructor); constructors.put(clazz, constructor);
ignore.remove(clazz); ignore.remove(clazz);
for (var el : prototypes.keySet()) {
if (clazz.isAssignableFrom(el)) {
updateProtoChain(el, getProto(el), getConstr(el));
}
}
} }
else { else {
prototypes.remove(clazz); prototypes.remove(clazz);