gwtproject / gwtproject/gwt

Ensure Java null isn't exposed to jsinterop as undefined

Open
#10,331 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
1.6k
Forks
397
Avg merge
4d 23h
Merged PRs (30d)
7

Description

GWT normalizes away JS null vs undefined so that Java code doesn't need to worry about the difference. There are two cases (that I can think of right now) where Java code can deal explicitly with undefined:

  • Js.undefined() gives Java the ability to ensure it has an undefined value, though it will show up as null in any Java == comparisons
  • Js.typeof(value) will return "undefined" when passed an undefined value (whereas null would result in "object"). The exception to this rule is document.all, due to a bug in IE that ended up being made into the spec.

To achieve this, Cast.maskUndefined is used in cases where the Java == or != operators could be used on a nullable reference.
https://github.com/gwtproject/gwt/blob/4e6ae10e9798e73fa490dabc64b7a742e933a66b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java#L293-L295

That is, if the compiler can't prove that one side or the other cannot be a null, it will replace the <reference> with maskUndefined(<reference>).

This means that when jsinterop returns an undefined into Java code (either as a property value, param to a method, return value from a method, etc), Java doesn't mind which, and can detect undefined (via value == null && Js.typeof(value).equals("undefined")). However, Java code that assigns properties, calls methods, or returns values to JS cannot guarantee that it is actually returning nulls vs undefineds - that is the topic of this issue.

This can be irritating for a few issues in JS - for example, === is usually the preferred comparison between objects in JS, yet while null === null and null == null both evaluate to true, null == undefined is true while null === undefined is false.

I propose that we add a configuration property (or compiler flag) to apply maskUndefined to any values as they are passed to native jsinterop. This will result in a small size increase in some cases, so probably should be opt-in.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with Cast.maskUndefined in dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java and trace where native jsinterop assignments, calls, and returns are generated. Define an opt-in configuration property or compiler flag that applies this behavior at those boundaries, then verify that null is preserved distinctly from undefined without enabling it by default.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.