skip to Main Content

UPDATE: Based on various feedback, I’m significantly updating my question so much so that it is different from (though still related to) the original one. I don’t know what the proper protocol is for this on StackOverflow, so I’m sorry if I’m doing the wrong thing.

I’m working in ServiceNow, a popular Software-as-a-Service platform implemented in Java that executes customer JavaScript code using the Rhino JavaScript engine. One of their APIs is GlideRecord, which is the API customers use to do CRUD operations against the MySQL database where all data is stored on the platform.

When I access a particular field value through the GlideRecord API, the field value is provided in the form of an object called GlideElement:

// JavaScript code
var gr = new GlideRecord('incident');
gr.setLimit(1);
gr.query();
gr.next();
var ge = gr.short_description; // GlideElement object

Through the magic of the Rhino engine, both GlideRecord and GlideElement are not JavaScript objects but Java objects somehow shared into the JavaScript runtime. The weird thing about the GlideElement object is it seems to somehow be an instance of java.lang.String:

gs.print(ge instanceof GlideElement);               // true
gs.print(ge instanceof Packages.java.lang.String);  // true

(The Packages object is supplied by Rhino as a way to access things like java.lang.String. This looks fishy, but as far as I know it is the real java.lang.String from Java, no funny business.)

My original question asked how something could be an instance of two classes and I now understand that’s an ordinary thing enabled by polymorphism (subclassing and interface implementation). However, the java.lang.String class is a final class, so it shouldn’t be subclassable or implementable by any other class.

Unfortunately the code above is the entirety of what I can provide because the SaaS platform’s Java code is not open source, so I can’t (and no one can) see how GlideRecord and GlideElement are implemented under the hood. Without access to the source code, what can we know or speculate about how this could have been implemented in either vanilla Java or some arcane witch magic enabled by the Rhino engine?

The code above can be executed if you have access to any ServiceNow instance. You can register for free and spin up a “personal developer instance” of ServiceNow at https://developer.servicenow.com. Once you have an instance you can navigate to All › System Definition › Scripts – Background to paste and execute arbitrary JavaScript code.

2

Answers


  1. Is there a pure Java way to get an object to pass both these instanceof tests?

    Yes, and it happens all the time. No magic or weird constructs necessary. Just extends or implements.

    Is there some other kind of magic going on here specific to the Rhino JavaScript engine?

    Could be; but you don’t need ‘rhino javascript engine magic’ to explain the notion that a single object is instanceof more than one type, because by definition every java object is except literally new Object():

    instanceof does not check ‘is the expression an instance of this exact class’. Name notwithstanding.

    it checks ‘is the expression an instance of this exact type or any class that is a subtype of it‘.

    The ‘simple’ (and incorrect) view (that it checks only if it is the exact class named) sounds nice but is almost entirely useless. It would imply that e.g. doing instanceof Number, or instanceof List is useless as it would necessarily return false (Number and List are interfaces / abstract classes; it’s impossible to create an object that is specifically an instance of them; new Number() is not legal, nor is new List()). Lots of design has all the actual concrete types as non-public. You craft them via factory constructors and the only public type available is a supertype of some sort. instanceof would be useless there.

    If x instanceof Type returns true, then you can cast x to Type and it will work. That’s a useful operation and is what instanceof does.

    Remember – class Y extends X means that Y is designed as: Take everything that class X{} defines. Every field, every method. Y has all of those things. Now Y can define even more things that Y has, but, it can’t take things away.

    In other words, given class Y extends X, the whole point of the exercise is that you can treat instances of Y as if they were an X. Because they are.

    The right terminology is is a. class Integer extends Number means "any instance of Integer is a Number". With that terminology, it’s obvious why Integer i = 5; System.out.println(i instanceof Number); prints true. Because i is a Number.

    Do you have a pet dog? Let’s say you do.

    Rover is a dog.

    Rover is an animal.

    rover instanceof Animal is true. That should be obvious, and logical. rover instanceof Dog is also true. Hopefully that makes inherent sense.

    But I want the specific class / check the specific impl!

    No problem. You’re looking for .getClass() instead of instanceof:

    Number n = Integer.valueOf(5);
    System.out.println(n instanceof Integer); // true
    System.out.println(n instanceof Number); // true
    System.out.println(n instanceof Object); // true
    System.out.println(n instanceof Double); // false
    System.out.println(n.getClass() == Integer.class); // true
    System.out.println(n.getClass() == Number.class); // false
    
    Login or Signup to reply.
  2. So, the first thing that we need to establish here is that you’re not using the Java instanceof — you’re using the JavaScript instanceof operator.

    So the operator you’re using is based on the JavaScript prototype chain, and will not return the same results as the Java instanceof.

    So what’s happening with the JavaScript instanceof? The bad news is, the prototype chain is not well-defined for Java objects in the Rhino environment, because Java objects in Rhino are ECMAScript "host objects," which do not need to obey ordinary JavaScript semantics. ("Host objects" with which you might be more familiar are things like window and document in the browser environment, which do not behave like ordinary JavaScript objects in a variety of ways.)

    Thinking about Java objects specifically, Java does not have prototype-based inheritance, so Java objects do not have prototypes like JavaScript objects do, and won’t behave exactly like JavaScript objects. To give an example, there’s no Packages.java.lang.Object.prototype even though
    there’s a new Packages.java.lang.Object(), and you certainly can’t assign properties to Packages.java.lang.Object.prototype and have new methods show up throughout the JVM, or even throughout Rhino.

    So you’d really have to start digging into source code if you wanted to figure out how it works, and it might well be version-dependent. I don’t know exactly what version of Rhino is bundled with ServiceNow (you might be able to hack into the Rhino subsystem and do something like Context.getCurrentContext().getImplementationVersion() in a script), but you’re going to rapidly get into esoterica if you start trying to figure out how the JavaScript prototype chain is modeled for Java objects in Rhino. Here’s a Stack Overflow question which discusses it for a user-defined JavaScript object implemented in Java. And here’s someone trying to use a Java object as a JavaScript prototype.

    Anyway, without learning more about your use case, it’s hard to say what parts of this will matter for you. But if you’re just curious about the wacky result you observed, the basic answer to your question is that the Java type system isn’t the basis for the instanceof you’re using.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search