mozilla / rhino

Rhino is an open-source implementation of JavaScript written entirely in Java
https://rhino.github.io
Other
4.18k stars 850 forks source link

please support java class access, eg: java.lang.Object.class #757

Open tangjfn opened 4 years ago

tonygermano commented 4 years ago

This would be nice to have, but there are a few workarounds if you need it right now.

If you have an instance of the class you want, you can call getClass() on it

var objectClass = new java.lang.Object().getClass()

You can get the current Thread's context ClassLoader and load the class from there.

If you pass java.lang.Object (I believe this is an instance of NativeJavaClass) as an argument to a java method, it will get converted to a java.lang.Class. So, if you need to pass it to a java method, you can let Rhino handle it for you. That also means if you are trying to get an instance of java.lang.Class to work within a Rhino script, you can do this,

var objectClass = java.util.Collections.singletonList(java.lang.Object).get(0);
tonygermano commented 4 years ago

just found another way to do it

var objectClass = org.mozilla.javascript.Context.jsToJava(java.lang.Object, java.lang.Class)
tangjfn commented 4 years ago

just found another way to do it

var objectClass = org.mozilla.javascript.Context.jsToJava(java.lang.Object, java.lang.Class)

thank you very mach!

tuchida commented 3 years ago
js> java.lang.Object.__javaObject__;
class java.lang.Object

https://github.com/mozilla/rhino/blob/5cf8b7f22e331d458985e56adf59b422e726e200/src/org/mozilla/javascript/NativeJavaClass.java#L33-L34

tonygermano commented 3 years ago

@tuchida thanks for pointing that out! Is there any reason why the property shouldn't just be class instead of __javaObject__? I think the only reason not to would be to avoid potential conflicts. However, I think that could only happen if there was a static field or method as part of the class definition named class, which should be prohibited since "class" is a keyword.

tuchida commented 3 years ago

I don't know why __javaObject__.

which should be prohibited since "class" is a keyword.

The ECMAScript specification allows for the use of reserved words in property names.

var a = { class: 123 };
a.class; // 123

Also, Rhino specification allows shorthand notation for beans, so you can write .getClass() as .class.

var o = java.lang.Object();
o.class; // class java.lang.Object
o.getClass() === o.class; // true
tonygermano commented 3 years ago

I meant "class" should be prohibited as a java static field or method on the underlying java class, so I think there should not be a conflict when trying to access a "class" property of the NativeJavaClass via the get method.

The NativeJavaClass represents the class itself, which is used to access static members and constructors. It doesn't have a getClass method, as only instances of Java classes do, so the shorthand does not work in this case.

tonygermano commented 3 years ago

I guess another reason to believe there is no chance of conflict is that java.lang.Object.class is exactly how you would accomplish obtaining an instance of java.lang.Class that refers to java.lang.Object in Java.

tuchida commented 3 years ago

I see. I think it was only in ES5 that it was specified that reserved words could be used in property names, so there must be some historical background.

tuchida commented 3 years ago

It seems that even now, depending on your settings, you cannot use reserved words as identifiers. https://github.com/mozilla/rhino/blob/b0fb5f8f775127cabbd8168bfe99d6ec3c2f309f/src/org/mozilla/javascript/TokenStream.java#L755-L761

tonygermano commented 3 years ago

I don't think properties are identifiers, so that shouldn't be a problem.

p-bakker commented 3 years ago

This works in Rhino, at least when using Context.VERSION_ES6 as language level: new java.lang.String().class.getField("CASE_INSENSITIVE_ORDER")

Can this issue be closed?

tonygermano commented 3 years ago

@p-bakker I think this is still an issue to be addressed. It's already been pointed out above that it works on an instance of a class (because it ends up calling the .getClass() method on the object.) The goal here is to get an instance of java.lang.Class without first needing to instantiate an object of the target class.

I don't see any reason to not add another property to NativeJavaObject called class that functions in exactly the same way as the __javaObject__ property. I'd just as soon change it if not for that whole backwards compatibility thing. This would make it work in the same way as a java class literal, which is the logical way to access it.

p-bakker commented 3 years ago

Ahh, missed that detail

Well, as all classes extend Object, Object defines a getClass() method and static methods cannot mirror instance methods and because 'class' in Java is a reserved word, so it cannot be used as the name of a (static) field, I personally don't see a (backwards or forward) compatibility issue with adding a 'class' property to NativeJavaObject that mimics the 'class' property of types in Java.

More self-explanatory than the obscure (and I think undocumented) '__javaObject__'.

Only question is from which Context.VERSION_Xxxx onwards we should, as using 'class' as a propertyName want always allowed I think

p-bakker commented 3 years ago

Mmm, I guess there is sort of an odd scenario: if there is a class out there that is used icw Rhino and accessing that class as a NativeJavaObject and a class defines a static void setClass(Class cls);.

Likelyhood of that happening? Very slim if you'd ask me.

We could support that scenario by in that case making the NativeJavaObject's class property a writable property (should be a non-writable property in all other cases I think)

rPraml commented 1 month ago

Only question is from which Context.VERSION_Xxxx onwards we should, as using 'class' as a propertyName want always allowed I think

Other question: ES6 is from 2015. We are in 2024 now. Are there still use cases, where you use rhino not in ES6 mode? Could we consider removing all previous script versions with e.g. Rhino 2.0? Then we wouldn't have this problem. Alternatively, we could force at least ES6 as a requirement for LiveConnect.

andreabergia commented 1 month ago

For us at ServiceNow, keeping old versions around and executing code in the same way is a fundamental requirement. Changing defaults is good, but (unfortunately) we need to maintain very strong backward compatibility.