Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-10829] Android: Case of infinite loop

GitHub Issuen/a
TypeBug
PriorityLow
StatusReopened
ResolutionUnresolved
Affected Version/sRelease 2.1.2, Release 3.1.0
Fix Version/sn/a
ComponentsAndroid
Labelsapi, exalture
ReporterIvan Skugor
AssigneeUnknown
Created2012-08-24T07:24:02.000+0000
Updated2018-07-11T20:58:34.000+0000

Description

The problem

Well, I don't know how to explain the problem, or what precisely is causing it ... so it's the best if I just jump to example. :D

Test case

var i = 0;

var win = {
    backgroundColor: '#000',
    __TiElement: null,
    __getTiElement: function() {
        if (!this.__TiElement) {
            this.__TiElement = Ti.UI.createWindow(this);
        }
        return this.__TiElement;
    },
    open: function() {
        Ti.API.info(i++);
        this.__getTiElement().open();
    }
};

win.open();
Run this example, and you should get infinite loop (see in DDMS, i goes from 0 to infinity). As I said, I'm not sure what's causing the problem, but it seems that "this" value is somehow set to wrong value (TiUIWindow because "win" and that causes infinite loop). The problem can be solved if non-enumerable properties are used (by using "Object.defineProperty"). The issue does not exist on iOS.

Comments

  1. jithinpv 2013-03-18

    infinite loop exists Issue reproduces with Titanium Studio, build: 3.0.1.201212181159 Titanium SDK version: 3.1.0 (03/11/13 15:43 0c88429) Titanium SDK version: 3.0.2 (02/07/13 16:46 a4def81) Device: Samsung galaxy s duos Android version: 4.0.4 jithinpv
  2. Sunila 2013-04-03

    By giving 'this' to the createWindow, the execution context is set to 'this' which is the 'win'. so calling 'open' on the __TiElement actually calls the win.open. The behavior can be reproduced in a standalone javascript by replacing Ti.UI.createWindow(this) with 'new Object(this)' and replacing Ti.API.info with 'alert'.
  3. Ivan Skugor 2013-04-03

    Hi Sunila. That is not the same thing. "win.open" is different function from "TiWindow.open" (at least it should be). Your explanation of the cause of infinite loop could be right, but the problem still exists (that is, it should not happen). The same thing works on iOS - how come? ;)
  4. Bryan Hughes 2013-04-08

    In the above example, win.open() does NOT set the this binding to the open window, or global object, or module object (if one exists). The this binding is set to the object "win", just as you would expect. At least that is what should happen according to the ECMAScript 5 spec. This is definitely a bug in Android. I'm reopening it. If whoever works on this bug doesn't understand the details of how the this pointer is set in a function call, then come talk to me. FWIW the way that the this pointer is set in JavaScript calls is somewhat simple and _completely_ not what you would expect it to be.
  5. Sunila 2013-05-07

    Looks like a JS implementation bug.
  6. Christopher Williams 2018-05-11

    Man is this an oldie. Anyhow, I believe this is because Android is applying this to the window proxy it generates (like it applies any supplied properties, not as the context to execute under) - therefore what happens is that the window instance has it's open property (the native one we supply to actually open a window) replaced by the wrapper (win object here) open function. So calling open() on the window proxy is going to result in an infinite loop. This actually relates to TIMOB-5818 in that I believe this is a result of Android and iOS having different property attributes on that open method. iOS defines The open method as not being writable which is why the wrapper can't overwrite the native method. Android gives this for the property descriptor:
       open = {
         "writable": true,
         "enumerable": true,
         "configurable": true
       }
       
    While iOS gives:
       open = {
         "writable": false,
         "enumerable": false,
         "configurable": true
       }
       
    (And will return that for made up properties on proxies that don't actually exist!)

JSON Source