JDK-8062132 : Nashorn incorrectly binds "this" for constructor created by another function
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.script
  • Affected Version: 8u20
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: os_x
  • CPU: x86
  • Submitted: 2014-10-24
  • Updated: 2017-07-27
  • Resolved: 2014-10-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u40Fixed 9 b39Fixed
Description
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Mac OS X 10.9.5

EXTRA RELEVANT SYSTEM CONFIGURATION :
Standard Macbook Pro setup

A DESCRIPTION OF THE PROBLEM :
When a higher order function is used to create JavaScript constructor functions, Nashorn incorrectly binds the "this" var within the created constructor function in some cases. This seems to be limited to cases where:

* Higher order function used to generate constructors
* Two child constructors share the same parent prototype.
* The two child prototypes disagree in the *number* of properties.

When the second of the two children is instantiated, its constructor's "this" var is incorrectly bound to the Parent's object rather than the Child's. To demonstrate this, I've included a reduction in the "Source code for an executable test case" section below.

One really strange aspect of this is that the two "Child" constructors must disagree in the *number* of properties to trigger the failure. Notice that Child1 has a single property "prop1", but Child2 has none. If you add any property to Child2, the error goes away. If you then add a property to Child1, the error returns.

This test case functions properly in all major browsers and in previous JDK version 1.8.0_05

REGRESSION.  Last worked in version 8u5

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Use jrunscript to run the reduction provided in the "Source code for an executable test case" section below.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The script should run without error, producing the following output:

!!! child 1
!!! child 2
ACTUAL -
It reaches line 18, where it tries calling "this.init()". This should work, since we're dealing with an instance of "Child2", which has overridden the "init" function. It fails, however, because "this" is incorrectly bound to the "Parent" object, for which "init" is null:

!!! child 1
script error in file scripts/test2.js : TypeError: null is not a function in scripts/nashorn_this_binding_bug_reduction.js at line number 18

ERROR MESSAGES/STACK TRACES THAT OCCUR :
script error in file scripts/test2.js : TypeError: null is not a function in scripts/nashorn_this_binding_bug_reduction.js at line number 18

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
function subclass(parentConstructor, proto) {
  function C() {
    parentConstructor.call(this);
  }
  
  C.prototype = Object.create(parentConstructor.prototype);

  for (var prop in proto) {
    if (proto.hasOwnProperty(prop)) {
      C.prototype[prop] = proto[prop];
    }
  }
  
  return C;
}

var Parent = function() {
  this.init();
};

Parent.prototype = {
  init: null
}

var Child1 = subclass(Parent, {
  prop1: 1,
  init: function() {
    print('!!! child 1');
  }
});

var Child2 = subclass(Parent, {
  init: function() {
    print('!!! child 2');
  }
});

new Child1();
new Child2();

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
No workaround available. Many 3rd party products use this type of inheritance pattern in JavaScript.


Comments
On 03.11.2015 14:27, Bhanu Gopularam wrote: > > Hi Ilya, > > The bug has started appearing 1.8.20 b02 to 1.8.20 b12. I can get you exact build number if you want, it is passing with 1.8.20 b01. > > I tested the use-case/scenario in earlier builds and it is passing. > > https://bugs.openjdk.java.net/browse/JDK-8062656 - Nashorn incorrectly binds "this" for constructor created by another function > > Please let me know if you need more information.
03-11-2015

The problem is that the callsite for the init() call is not invalidated on the second call. If both child objects have the same properties, this does not matter because the init function sits in the same slot. However, if the property maps differ invocation fails.
30-10-2014

Confirms this issue with 8u25 and 8u40. Test with 7u71 and 8u5 ran successfully.
27-10-2014