#258908: Treat undefined as null
authorJaroslav Tulach <jtulach@netbeans.org>
Tue, 19 Apr 2016 20:31:18 +0200
changeset 1083b49546d64269
parent 1082 1b03b68f551f
child 1084 82ec5550b863
#258908: Treat undefined as null
boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java
boot/src/main/java/net/java/html/js/package.html
json-tck/src/main/java/net/java/html/js/tests/Bodies.java
json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java
json-tck/src/main/java/net/java/html/js/tests/Sum.java
src/main/javadoc/overview.html
     1.1 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java	Sun Apr 17 08:06:55 2016 +0200
     1.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java	Tue Apr 19 20:31:18 2016 +0200
     1.3 @@ -76,6 +76,7 @@
     1.4      // transient - e.g. not cloneable
     1.5      private JSObject arraySize;
     1.6      private JSObject wrapArrImpl;
     1.7 +    private Object undefined;
     1.8  
     1.9      @Override
    1.10      protected AbstractFXPresenter clone() {
    1.11 @@ -83,6 +84,7 @@
    1.12              AbstractFXPresenter p = (AbstractFXPresenter) super.clone();
    1.13              p.arraySize = null;
    1.14              p.wrapArrImpl = null;
    1.15 +            p.undefined = null;
    1.16              return p;
    1.17          } catch (CloneNotSupportedException ex) {
    1.18              throw new IllegalStateException(ex);
    1.19 @@ -223,6 +225,13 @@
    1.20          return wrapArrImpl;
    1.21      }
    1.22  
    1.23 +    final Object undefined() {
    1.24 +        if (undefined == null) {
    1.25 +            undefined = engine.executeScript("undefined");
    1.26 +        }
    1.27 +        return undefined;
    1.28 +    }
    1.29 +
    1.30      final Object checkArray(Object val) {
    1.31          if (!(val instanceof JSObject)) {
    1.32              return val;
    1.33 @@ -233,8 +242,22 @@
    1.34          }
    1.35          Object[] arr = new Object[length];
    1.36          arraySizeFn().call("array", val, arr);
    1.37 +        clearUndefinedArray(arr);
    1.38          return arr;
    1.39      }
    1.40 +
    1.41 +    private void clearUndefinedArray(Object[] arr) {
    1.42 +        for (int i = 0; i < arr.length; i++) {
    1.43 +            if (arr[i] == undefined) {
    1.44 +                arr[i] = null;
    1.45 +                continue;
    1.46 +            }
    1.47 +            if (arr[i] instanceof Object[]) {
    1.48 +                clearUndefinedArray((Object[])arr[i]);
    1.49 +            }
    1.50 +        }
    1.51 +    }
    1.52 +
    1.53      private final JSObject arraySizeFn() {
    1.54          if (arraySize == null) {
    1.55              try {
    1.56 @@ -328,6 +351,7 @@
    1.57  
    1.58          final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
    1.59              try {
    1.60 +                final AbstractFXPresenter presenter = (AbstractFXPresenter) presenter();
    1.61                  if (LOG.isLoggable(Level.FINE)) {
    1.62                      LOG.log(Level.FINE, "calling {0} function #{1}", new Object[]{++call, id});
    1.63                      LOG.log(Level.FINER, "  thiz  : {0}", thiz);
    1.64 @@ -340,7 +364,7 @@
    1.65                      if (arrayChecks) {
    1.66                          if (args[i] instanceof Object[]) {
    1.67                              Object[] arr = (Object[]) args[i];
    1.68 -                            conv = ((AbstractFXPresenter)presenter()).convertArrays(arr);
    1.69 +                            conv = presenter.convertArrays(arr);
    1.70                          }
    1.71                          if (conv != null && keepAlive != null &&
    1.72                              !keepAlive[i] && !isJSReady(conv) &&
    1.73 @@ -355,13 +379,13 @@
    1.74                  if (ret instanceof Weak) {
    1.75                      ret = ((Weak)ret).get();
    1.76                  }
    1.77 -                if (ret == fn) {
    1.78 +                if (ret == fn || ret == presenter.undefined()) {
    1.79                      return null;
    1.80                  }
    1.81                  if (!arrayChecks) {
    1.82                      return ret;
    1.83                  }
    1.84 -                return ((AbstractFXPresenter)presenter()).checkArray(ret);
    1.85 +                return presenter.checkArray(ret);
    1.86              } catch (Error t) {
    1.87                  t.printStackTrace();
    1.88                  throw t;
     2.1 --- a/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java	Sun Apr 17 08:06:55 2016 +0200
     2.2 +++ b/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java	Tue Apr 19 20:31:18 2016 +0200
     2.3 @@ -175,6 +175,7 @@
     2.4          fn.invokeImpl(null, false, val, arr);
     2.5          return arr;
     2.6      }
     2.7 +
     2.8      private FnImpl arraySize;
     2.9      private FnImpl arraySizeFn() {
    2.10          if (arraySize == null) {
    2.11 @@ -185,7 +186,9 @@
    2.12                      + "  else return -1;\n"
    2.13                      + "} else {\n"
    2.14                      + "  var l = arr.length;\n"
    2.15 -                    + "  for (var i = 0; i < l; i++) to[i] = arr[i];\n"
    2.16 +                    + "  for (var i = 0; i < l; i++) {\n"
    2.17 +                    + "    to[i] = arr[i] === undefined ? null : arr[i];\n"
    2.18 +                    + "  }\n"
    2.19                      + "  return l;\n"
    2.20                      + "}", new String[] { "arr", "to" }, null
    2.21                  );
     3.1 --- a/boot/src/main/java/net/java/html/js/package.html	Sun Apr 17 08:06:55 2016 +0200
     3.2 +++ b/boot/src/main/java/net/java/html/js/package.html	Tue Apr 19 20:31:18 2016 +0200
     3.3 @@ -341,6 +341,29 @@
     3.4          to JavaScript by passing it as a parameter of some method 
     3.5          (like the <code>setValue</code> one) and perform necessary JavaScript
     3.6          calls or changes on it.
     3.7 +
     3.8 +        <h3>undefined === null</h3>
     3.9 +        <a name='undefined'></a>
    3.10 +
    3.11 +        JavaScript recognizes two <em>empty</em> values: <code>null</code> and
    3.12 +        <code>undefined</code>. Java has just <code>null</code>.
    3.13 +
    3.14 +        For purposes of simplicity and easier inter-operability, <code>undefined</code>
    3.15 +        values returned from {@link net.java.html.js.JavaScriptBody @JavaScriptBody}
    3.16 +        annotated methods are converted to <code>null</code>. In the following
    3.17 +        example both methods return <code>null</code>:
    3.18 +<pre>
    3.19 +  {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(
    3.20 +    args = {}, body = "var empty = {}; return empty.x;"
    3.21 +  )
    3.22 +  <b>private static native</b> Object returnUndefined();
    3.23 +  {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(
    3.24 +    args = {}, body = "var empty = {}; empty.x = null; return empty.x;"
    3.25 +  )
    3.26 +  <b>private static native</b> Object returnNull();
    3.27 +}
    3.28 +</pre>
    3.29 +        This is the behavior since version 1.4.
    3.30          
    3.31          <h3>Post Process Classes</h3>
    3.32          <a name="post-process"></a>
     4.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Bodies.java	Sun Apr 17 08:06:55 2016 +0200
     4.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Bodies.java	Tue Apr 19 20:31:18 2016 +0200
     4.3 @@ -192,6 +192,32 @@
     4.4      )
     4.5      static native int gc(double max);
     4.6  
     4.7 +    @JavaScriptBody(args = {}, body = ""
     4.8 +        + "var o = {};\n"
     4.9 +        + "return o.x;\n"
    4.10 +    )
    4.11 +    static native Object unknown();
    4.12 +
    4.13 +    @JavaScriptBody(args = {}, body = ""
    4.14 +        + "return new Array(2);\n"
    4.15 +    )
    4.16 +    static native Object[] unknownArray();
    4.17 +
    4.18 +    @JavaScriptBody(args = { "sum" }, javacall = true, body = ""
    4.19 +        + "var arr = [];\n"
    4.20 +        + "arr[1] = null;\n"
    4.21 +        + "arr[2] = 1;\n"
    4.22 +        + "return sum.@net.java.html.js.tests.Sum::sumNonNull([Ljava/lang/Object;)(arr);\n"
    4.23 +    )
    4.24 +    static native int sumNonNull(Sum sum);
    4.25 +
    4.26 +    @JavaScriptBody(args = { "sum", "p" }, javacall = true, body = ""
    4.27 +        + "var obj = {};\n"
    4.28 +        + "obj.x = 1;\n"
    4.29 +        + "return sum.@net.java.html.js.tests.Sum::checkNonNull(Ljava/lang/Object;)(obj[p]);\n"
    4.30 +    )
    4.31 +    static native boolean nonNull(Sum sum, String p);
    4.32 +
    4.33      @JavaScriptBody(args = {}, javacall = true, body = 
    4.34          "return @net.java.html.js.tests.Bodies::problematicString()();"
    4.35      )
     5.1 --- a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java	Sun Apr 17 08:06:55 2016 +0200
     5.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java	Tue Apr 19 20:31:18 2016 +0200
     5.3 @@ -358,7 +358,42 @@
     5.4          String all = Bodies.primitiveTypes(new Sum());
     5.5          assertEquals("Ahojfalse12356.07.0 TheEND", all, "Valid return type: " + all);
     5.6      }
     5.7 +
     5.8 +    @KOTest public void returnUnknown() {
     5.9 +        Object o = Bodies.unknown();
    5.10 +        assertNull(o, "Unknown is converted to null");
    5.11 +    }
    5.12 +
    5.13 +    @KOTest public void returnUndefinedString() {
    5.14 +        Object o = Bodies.id("undefined");
    5.15 +        assertNotNull(o, "String remains string");
    5.16 +    }
    5.17 +
    5.18 +    @KOTest public void returnUnknownArray() {
    5.19 +        Object[] arr = Bodies.unknownArray();
    5.20 +        assertEquals(arr.length, 2, "Two elements");
    5.21 +        assertNull(arr[0], "1st element is null");
    5.22 +        assertNull(arr[1], "2nd element is null");
    5.23 +    }
    5.24 +
    5.25 +    @KOTest public void callbackKnown() {
    5.26 +        Sum s = new Sum();
    5.27 +        boolean nonNull = Bodies.nonNull(s, "x");
    5.28 +        assertTrue(nonNull, "x property exists");
    5.29 +    }
    5.30      
    5.31 +    @KOTest public void callbackUnknown() {
    5.32 +        Sum s = new Sum();
    5.33 +        boolean isNull = Bodies.nonNull(s, "y");
    5.34 +        assertTrue(isNull, "y property doesn't exist");
    5.35 +    }
    5.36 +
    5.37 +    @KOTest public void callbackUnknownArray() {
    5.38 +        Sum s = new Sum();
    5.39 +        int nullAndUnknown = Bodies.sumNonNull(s);
    5.40 +        assertEquals(nullAndUnknown, 1, "Only one slot");
    5.41 +    }
    5.42 +
    5.43      @KOTest public void problematicString() {
    5.44          String orig = Bodies.problematicString();
    5.45          String js = Bodies.problematicCallback();
     6.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Sum.java	Sun Apr 17 08:06:55 2016 +0200
     6.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Sum.java	Tue Apr 19 20:31:18 2016 +0200
     6.3 @@ -60,6 +60,20 @@
     6.4          }
     6.5          return s;
     6.6      }
     6.7 +
     6.8 +    public int sumNonNull(Object[] arr) {
     6.9 +        int s = 0;
    6.10 +        for (int i = 0; i < arr.length; i++) {
    6.11 +            if (arr[i] != null) {
    6.12 +                s++;
    6.13 +            }
    6.14 +        }
    6.15 +        return s;
    6.16 +    }
    6.17 +
    6.18 +    public boolean checkNonNull(Object obj) {
    6.19 +        return obj != null;
    6.20 +    }
    6.21      
    6.22      public String all(boolean z, byte b, short s, int i, long l, float f, double d, char ch, String str) {
    6.23          return "Ahoj" + z + b + s + i + l + f + d + ch + str;
     7.1 --- a/src/main/javadoc/overview.html	Sun Apr 17 08:06:55 2016 +0200
     7.2 +++ b/src/main/javadoc/overview.html	Tue Apr 19 20:31:18 2016 +0200
     7.3 @@ -75,6 +75,11 @@
     7.4           yet the application code can be written in Java.
     7.5          </p>
     7.6  
     7.7 +        <h3>New features in version 1.4</h3>
     7.8 +
     7.9 +        Both values <code>null</code> and <code>undefined</code> are
    7.10 +        <a href="net/java/html/js/package-summary.html#undefined">treated as null</a>.
    7.11 +
    7.12          <h3>Improvements in version 1.3</h3>
    7.13  
    7.14          {@link net.java.html.json.Model Model classes} can have