graaljs icon indicating copy to clipboard operation
graaljs copied to clipboard

How to implement custom built-in classes/functions

Open tuzzmaniandevil opened this issue 3 years ago • 3 comments

I'm working on re-creating our current JS App engine which is based on Nashorn to use GraalJS, But I need to implement some Web API/Service Worker standards like fetch, Crypto, console, etc

Some notes, We are using the JS engine from GraalVM, Not the NodeJS version. We don't need to use ScriptEngine either as we already have an abstraction layer that allows us to plug in different engines.

I want to try keep my implementations as close to the specifications and how similar platforms like Deno, CloudFlare and the browser implement these API's to ensure a better compatibility with npm packages.

So my question is, How to I implement builtins or native objects/functions in GraalJS?

I've looked into using ProxyObject and ProxyExecute, Here are some of the issues I'm having:

  1. I can't figure out how to set the Symbol.toStringTag property of a ProxyObject. This property identifies the class name essentially. e.g. console.log(Object.prototype.toString.call(crypto)) outputs [object Crypto] in a browser. I found the Symbol class in GraalJS and the necessary values. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
  2. How do I throw a JS TypeError exception from Java? For example if the wrong arguments get passed to the Headers class (Used in fetch API) it should throw an exception that reads: TypeError: Failed to construct 'Headers': The provided value is not of type '(record<ByteString, ByteString> or sequence<sequence<ByteString>>)'. I'm currently throwing an IllegalArgumentException which isn't ideal.

Any advice or assistance would be great :-)

tuzzmaniandevil avatar Jul 26 '22 05:07 tuzzmaniandevil

Hi, Thank you for reaching out, let me check and I'll get back to you

oubidar-Abderrahim avatar Aug 01 '22 07:08 oubidar-Abderrahim

I can't figure out how to set the Symbol.toStringTag property of a ProxyObject.

This is not possible. ProxyObject (like other parts of Polyglot API) expects members to use String keys. On the other hand, you can wrap your ProxyObject in an appropriate JavaScript Proxy and use this Proxy instead:

handler = { get(target, prop, recv) { return (prop === Symbol.toStringTag) ? 'MyTag' : target[prop]; } };
proxy = new Proxy(yourProxyObject, handler);

proxy delegates member/property read/writes to yourProxyObject and Object.prototype.toString.call(proxy) is [object MyTag].

iamstolis avatar Aug 01 '22 08:08 iamstolis

How do I throw a JS TypeError exception from Java?

Just get the reference to TypeError:

Value typeError = context.eval("js", "TypeError");

and use

throw typeError.newInstance(someErrorMessage).throwException();

iamstolis avatar Aug 01 '22 08:08 iamstolis