Compare commits
12 Commits
0.9.19-bet
...
0.9.30-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
ece9cf68dc
|
|||
|
11ecd8c68f
|
|||
|
48bd304c6e
|
|||
|
d8e46c3149
|
|||
|
5fc5eb08f8
|
|||
|
8acbc003c4
|
|||
|
fda33112a7
|
|||
|
67b2413d7c
|
|||
|
3a05416510
|
|||
|
c291328cc3
|
|||
|
7cb267b0d9
|
|||
|
4e31766665
|
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
18
src/java/me/topchetoeu/jscript/lib/ThrowableLib.java
Normal file
18
src/java/me/topchetoeu/jscript/lib/ThrowableLib.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -137,6 +137,13 @@ public class DebugServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runAsync(() -> {
|
runAsync(() -> {
|
||||||
|
var handle = new Thread(() -> {
|
||||||
|
System.out.println("test");
|
||||||
|
debugger.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(handle);
|
||||||
|
|
||||||
try { handle(ws, debugger); }
|
try { handle(ws, debugger); }
|
||||||
catch (RuntimeException | IOException e) {
|
catch (RuntimeException | IOException e) {
|
||||||
try {
|
try {
|
||||||
@@ -145,7 +152,11 @@ public class DebugServer {
|
|||||||
}
|
}
|
||||||
catch (IOException e2) { /* Shit outta luck */ }
|
catch (IOException e2) { /* Shit outta luck */ }
|
||||||
}
|
}
|
||||||
finally { ws.close(); debugger.close(); }
|
finally {
|
||||||
|
Runtime.getRuntime().removeShutdownHook(handle);
|
||||||
|
ws.close();
|
||||||
|
debugger.close();
|
||||||
|
}
|
||||||
}, "Debug Handler");
|
}, "Debug Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +175,6 @@ public class DebugServer {
|
|||||||
var req = HttpRequest.read(socket);
|
var req = HttpRequest.read(socket);
|
||||||
|
|
||||||
if (req == null) continue;
|
if (req == null) continue;
|
||||||
|
|
||||||
switch (req.path) {
|
switch (req.path) {
|
||||||
case "/json/version":
|
case "/json/version":
|
||||||
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
||||||
|
|||||||
@@ -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}"
|
||||||
);
|
);
|
||||||
@@ -50,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}}";
|
||||||
|
|
||||||
@@ -83,7 +87,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 +97,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))
|
||||||
@@ -146,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;
|
||||||
|
|
||||||
@@ -176,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;
|
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;
|
||||||
}
|
}
|
||||||
@@ -202,8 +218,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 +243,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 +310,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,22 +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 newCtx = new Context();
|
env = sanitizeEnvironment(env);
|
||||||
newCtx.addAll(ctx);
|
var ctx = env.context();
|
||||||
newCtx.add(DebugContext.IGNORE);
|
|
||||||
ctx = newCtx;
|
|
||||||
|
|
||||||
if (val == Values.NULL) {
|
if (val == Values.NULL) {
|
||||||
return new JSONMap()
|
return new JSONMap()
|
||||||
@@ -348,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;
|
||||||
@@ -366,6 +380,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() + ")");
|
||||||
@@ -376,16 +391,16 @@ 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) { }
|
||||||
|
|
||||||
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) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -411,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) {
|
||||||
@@ -451,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]";
|
||||||
@@ -463,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;
|
||||||
@@ -524,30 +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 {
|
||||||
engine.run(true);
|
engine.run(true);
|
||||||
|
return new RunResult(env, awaiter.await(), null);
|
||||||
try { return new RunResult(ctx, 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(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)) {
|
||||||
@@ -630,15 +657,18 @@ 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;
|
||||||
|
|
||||||
// idToBptCand.clear();
|
mappings.clear();
|
||||||
|
bpLocs.clear();
|
||||||
|
|
||||||
idToBreakpoint.clear();
|
idToBreakpoint.clear();
|
||||||
// locToBreakpoint.clear();
|
|
||||||
// tmpBreakpts.clear();
|
|
||||||
|
|
||||||
filenameToId.clear();
|
filenameToId.clear();
|
||||||
idToSource.clear();
|
idToSource.clear();
|
||||||
@@ -656,6 +686,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,17 +742,16 @@ 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) {
|
|
||||||
|
for (var loc : bpt.resolvedLocations.values()) {
|
||||||
locs.add(serializeLocation(loc));
|
locs.add(serializeLocation(loc));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ws.send(msg.respond(new JSONMap()
|
ws.send(msg.respond(new JSONMap()
|
||||||
.set("breakpointId", bpt.id + "")
|
.set("breakpointId", bpt.id + "")
|
||||||
@@ -730,6 +762,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 {
|
||||||
@@ -818,11 +851,13 @@ 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) {
|
||||||
|
while (obj != null) {
|
||||||
for (var key : obj.keys(true)) {
|
for (var key : obj.keys(true)) {
|
||||||
var propDesc = new JSONMap();
|
var propDesc = new JSONMap();
|
||||||
|
|
||||||
@@ -830,8 +865,8 @@ public class SimpleDebugger implements Debugger {
|
|||||||
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);
|
||||||
@@ -839,27 +874,33 @@ public class SimpleDebugger implements Debugger {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
propDesc.set("name", Values.toString(ctx, key));
|
propDesc.set("name", Values.toString(ctx, key));
|
||||||
propDesc.set("value", serializeObj(ctx, Values.getMember(ctx, obj, key)));
|
propDesc.set("value", serializeObj(env, Values.getMember(ctx, obj, key)));
|
||||||
propDesc.set("writable", obj.memberWritable(key));
|
propDesc.set("writable", obj.memberWritable(key));
|
||||||
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", own);
|
||||||
res.add(propDesc);
|
res.add(propDesc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var proto = Values.getPrototype(ctx, obj);
|
var proto = Values.getPrototype(ctx, obj);
|
||||||
|
|
||||||
|
if (own) {
|
||||||
var protoDesc = new JSONMap();
|
var protoDesc = new JSONMap();
|
||||||
protoDesc.set("name", "__proto__");
|
protoDesc.set("name", "__proto__");
|
||||||
protoDesc.set("value", serializeObj(ctx, proto == null ? Values.NULL : proto));
|
protoDesc.set("value", serializeObj(env, proto == null ? Values.NULL : proto));
|
||||||
protoDesc.set("writable", true);
|
protoDesc.set("writable", true);
|
||||||
protoDesc.set("enumerable", false);
|
protoDesc.set("enumerable", false);
|
||||||
protoDesc.set("configurable", false);
|
protoDesc.set("configurable", false);
|
||||||
protoDesc.set("isOwn", true);
|
protoDesc.set("isOwn", own);
|
||||||
res.add(protoDesc);
|
res.add(protoDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj = proto;
|
||||||
|
own = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ws.send(msg.respond(new JSONMap().set("result", res)));
|
ws.send(msg.respond(new JSONMap().set("result", res)));
|
||||||
}
|
}
|
||||||
@Override public synchronized void callFunctionOn(V8Message msg) throws IOException {
|
@Override public synchronized void callFunctionOn(V8Message msg) throws IOException {
|
||||||
@@ -874,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=");
|
||||||
@@ -892,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 {
|
||||||
@@ -958,11 +1003,12 @@ 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) {
|
||||||
pauseDebug(ctx, null);
|
pauseDebug(ctx, null);
|
||||||
@@ -1033,6 +1079,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,25 +279,28 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
|
private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
|
||||||
var parent = clazz;
|
var parent = clazz;
|
||||||
|
|
||||||
while (true) {
|
while (parent != null) {
|
||||||
parent = parent.getSuperclass();
|
|
||||||
if (parent == null) return;
|
|
||||||
|
|
||||||
var parentProto = getProto(parent);
|
var parentProto = getProto(parent);
|
||||||
var parentConstr = getConstr(parent);
|
var parentConstr = getConstr(parent);
|
||||||
|
|
||||||
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto);
|
if (parentProto != null && parentConstr != null) {
|
||||||
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr);
|
Values.setPrototype(Context.NULL, proto, parentProto);
|
||||||
|
Values.setPrototype(Context.NULL, constr, parentConstr);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = parent.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,8 +321,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 +345,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 +364,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);
|
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()) {
|
for (var el : prototypes.keySet()) {
|
||||||
if (clazz.isAssignableFrom(el)) {
|
if (clazz.isAssignableFrom(el)) {
|
||||||
updateProtoChain(el, getProto(el), getConstr(el));
|
updateProtoChain(el, getProto(el), getConstr(el));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
prototypes.remove(clazz);
|
|
||||||
constructors.remove(clazz);
|
|
||||||
ignore.remove(clazz);
|
|
||||||
|
|
||||||
initType(clazz, constructor, proto);
|
public NativeWrapperProvider() { }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initError() {
|
|
||||||
var proto = new ObjectValue();
|
|
||||||
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);
|
|
||||||
constr.defineProperty(null, "prototype", proto, true, false, false);
|
|
||||||
|
|
||||||
set(Throwable.class, proto, constr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeWrapperProvider(Environment env) {
|
|
||||||
this.env = env;
|
|
||||||
initError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user