Re: the ScriptingMojo

From: Chapman Flack <chap(at)anastigmatix(dot)net>
To: Kartik Ohri <kartikohri13(at)gmail(dot)com>
Cc: pljava-dev(at)lists(dot)postgresql(dot)org
Subject: Re: the ScriptingMojo
Date: 2020-08-25 23:14:54
Message-ID: 5F459B6E.3020803@anastigmatix.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pljava-dev

On 08/25/20 17:06, Kartik Ohri wrote:
> Hi!
>
>
>> - I don't think formatDefines and formatIncludes make good PGXSUtils
>> methods. The syntax could vary across compilers (might it use /D or /I
>> on a Windows compiler, for example?). So I think they make better
>> AbstractPGXS methods that can be overridden in the configuration.
>> There could be a built-in implementation in AbstractPGXS using -D and -I.
>> Defines should be provided as a map (key to value) and the option string
>> with = sign should be produced by formatting.
>>
>> Regarding this, it seems that it is not possible to invoke a method whose
> implementation
> is provided in the Java class before an object is created. (Same for
> default methods in an
> interface) So, compile itself cannot call a formatDefines if a default
> implementation is given using
> AbstractPGXS. However, formatDefines can be called separately and the value
> be passed to compile.

The way I imagined it working, there would be hardly any methods on the
JS object that would be expected to be called before using it to
instantiate an AbstractPGXS subclass. Only for the probe method did that
seem convenient.

What I had imagined was a dispatcher that would select one of the JS
objects, by calling their probe methods, and then use the selected one
to make an AbstractPGXS instance, and call other methods like compile
and link on that instance.

I've tried the basic idea in nashorn and jshell, like this:

jshell> import javax.script.ScriptEngineManager

jshell> var sem = new ScriptEngineManager()
sem ==> javax(dot)script(dot)ScriptEngineManager(at)5a5a729f

jshell> var se = sem.getEngineByName("javascript")
Warning: Nashorn engine is planned to be removed from a future JDK release
se ==> jdk(dot)nashorn(dot)api(dot)scripting(dot)NashornScriptEngine(at)6f6745d6

jshell> public abstract class Foo { public abstract int foo(); public
abstract void bar(); public void baz() { System.out.println("Whee!"); } }
| created class Foo

/*
* classes declared in jshell have weird names, so the only way I know to
* make Foo known to JS is with these 2 steps a normal class wouldn't need.
*/

jshell> se.put("FooClass", Foo.class)

jshell> se.eval("var Foo = FooClass.static")
$6 ==> null

jshell> se.eval("var obj = {foo: function(){return 42;}, bar:
function(){this.baz();}}")
$7 ==> null

/*
* obj is an ordinary JS object. Its foo method already works.
*/
jshell> se.eval("obj.foo()")
$8 ==> 42

/*
* its bar method doesn't, because there's no baz method.
*/
jshell> se.eval("obj.bar()")
| Exception javax.script.ScriptException: TypeError: this.baz is not a
function in <eval> at line number 1

/*
* Now make bigobj be a subclass of Foo, based on obj.
*/

jshell> se.eval("var bigobj = new Foo(obj)")
$10 ==> null

jshell> se.eval("bigobj.foo()")
$11 ==> 42

jshell> se.eval("bigobj.baz()")
Whee!
$12 ==> null

So that looks like it works ok. One thing I noticed is that bigobj does
not keep any JS methods from obj, if they aren't declared in the Java
class. So, for example, if I declare the Java class Foo without its foo
method, I can still call obj.foo(), but not bigobj.foo(). The adapter
magic only exposes the exact API declared by the Java object.

But that seems ok. For example, we could leave the probe method undeclared
in AbstractPGXS. Every JS recipe would have a probe method, and then when
the selected one is used to make a PGXS object, that doesn't have an
accessible probe method anymore, which it doesn't need anymore anyway.

I have noticed a quirk (in nashorn anyway, haven't tried graal) where
even the methods that are declared in the superclass can have trouble
referring to each other as this.method() even though calling them from
outside as bigobj.method() works fine. The dynamic nature of javascript
allows a workaround: they can refer to each other as bigobj.method().

I think that should be workable, as we will only be making one instance
of one of these objects, and probably saving that in an outer var, maybe
pgxs, and so if its JS-implemented methods internally refer to each other
it can just look like methoda: function() { pgxs.methodb(); }.

Regards,
-Chap

In response to

Responses

Browse pljava-dev by date

  From Date Subject
Next Message Kartik Ohri 2020-08-26 04:44:45 Re: the ScriptingMojo
Previous Message Kartik Ohri 2020-08-25 21:06:33 Re: the ScriptingMojo