JavetProxyConverter and instanceof not working
Closed this issue · 8 comments
When using the JavetProxyConverter
, any instanceof
call of proxied Java classes fails.
Simple example that throws an error:
final var javetProxyConverter = new JavetProxyConverter();
v8Runtime.setConverter(javetProxyConverter);
final var globalObject = v8Runtime.getGlobalObject();
globalObject.set("URL", URI.class);
v8Runtime.getExecutor(
// language=javascript
"let url = new URL('https://example.com');\n"
+ "if (!(url instanceof URL)) {\n"
+ " throw new Error('url is not instance of URL');\n"
+ "}"
).executeVoid();
Thank you for raising this issue. It is not supported, so far. You may use Class.isAssignableFrom()
instead.
But, that is technically doable and could be a feature request.
Update
I just reviewed the source code. It's technically impossible to make instanceof
work because the right hand is a Proxy
and the left hand cannot be a Proxy
. So, the result is always false
.
Why cannot the left hand be a Proxy
? V8 is designed to recursively traverse the proxied object for its original prototype. If the underlying API always returns a Proxy
, V8 will hang because of OOM (not stack overflow).
So, please try the workaround in my previous comment. Please let me know if you have any questions.
The instanceof
is part of a third-party library. I cannot modify the source.
I'm a bit confused about the proxy thing.
The script runs in V8 Mode (I have missed this info in my initial post), so URL
does not exist.
I tried to "polyfill" the URL
with globalObject.set("URL", URI.class);
.
Now I can do something like new URL(..)
in JS. and this creates a java URI object?!
In my expectation, the left side is a proxy to Javas URI class?
Currently, I have worked around by implementing a JS based polyfill URL
class.
I suggest you review this slide for how things work behind the scene in Javet. Actually, the one you create in V8 is a Proxy
. The pseudo logic in V8 is as follows:
function getPrototypeOf(obj) {
while (obj is proxy) {
obj = obj.target;
}
return obj.prototype;
}
As the object created by Javet is a proxy, the prototype is also a proxy. In your case, that is Proxy(URL.class)
. That while
loop will never end. That causes OOM in V8.
I suggest you review this slide for how things work behind the scene in Javet. Actually, the one you create in V8 is a
Proxy
. The pseudo logic in V8 is as follows:function getPrototypeOf(obj) { while (obj is proxy) { obj = obj.target; } return obj.prototype; }As the object created by Javet is a proxy, the prototype is also a proxy. In your case, that is
Proxy(URL.class)
. Thatwhile
loop will never end. That causes OOM in V8.
instanceof
运算符右侧是proxy似乎也能正常工作,只需将右侧的proxy对象设置一个prototype
属性,然后将此属性加入到实例对象的原型链中
let U = function(){}
let p = {
constructor: U
}
U.prototype = p
let c = new Proxy(U, {});
let r = Object.create(p);
console.log(r instanceof c);//true
Hi @tristanlins, good news! I was inspired by @aiselp 's comment and spent a week investigating this issue. It's now supported at the dev branch. This new feature passes the following assertions.
v8Runtime.getGlobalObject().set("StringBuilder", StringBuilder.class);
assertTrue(v8Runtime.getExecutor("StringBuilder instanceof StringBuilder;").executeBoolean());
assertTrue(v8Runtime.getExecutor("new StringBuilder() instanceof StringBuilder;").executeBoolean());
assertTrue(v8Runtime.getExecutor("Object.getPrototypeOf(new StringBuilder()) === Object.getPrototypeOf(StringBuilder);").executeBoolean());
v8Runtime.getGlobalObject().delete("StringBuilder");
That's a really tough issue. You may try the latest snapshot build and let me know if you have any questions. If my works pleases you, please consider Buy Me a Cup of Coffee at the donation link. Thank you.
@caoccao 感谢作者辛苦研究,上面的test包含以下内容或许更符合js标准
v8Runtime.getGlobalObject().set("StringBuilder", StringBuilder.class);
assertTrue(v8Runtime.getExecutor("new StringBuilder() instanceof StringBuilder;").executeBoolean());
assertTrue(v8Runtime.getExecutor("Object.getPrototypeOf(new StringBuilder()) === StringBuilder.prototype;").executeBoolean());
assertTrue(v8Runtime.getExecutor(
"let a = new StringBuilder() \n"+
"StringBuilder.prototype.abc= function(){
retun this === a
};\n"+
"a.abc()"
).executeBoolean());
v8Runtime.getGlobalObject().delete("StringBuilder");
Object.getPrototypeOf(StringBuilder)
should retun null or Function.prototype
Nice catch. There's a flaw in the implementation. It's now corrected as follows.
v8Runtime.getGlobalObject().set("StringBuilder", StringBuilder.class);
assertTrue(v8Runtime.getExecutor("new StringBuilder() instanceof StringBuilder;").executeBoolean());
assertTrue(v8Runtime.getExecutor("Object.getPrototypeOf(new StringBuilder()) === StringBuilder.prototype;").executeBoolean());
assertFalse(v8Runtime.getExecutor("Object.getPrototypeOf(new StringBuilder()) === Object.getPrototypeOf(StringBuilder);").executeBoolean());
assertFalse(v8Runtime.getExecutor("StringBuilder instanceof StringBuilder;").executeBoolean());
v8Runtime.getGlobalObject().delete("StringBuilder");