Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-25899] Android: Modify JSON.stringify(Error) to not return "{}"

GitHub Issuen/a
TypeImprovement
PriorityLow
StatusClosed
ResolutionFixed
Resolution Date2018-09-19T21:34:55.000+0000
Affected Version/sn/a
Fix Version/sRelease 7.5.0
ComponentsAndroid
Labelsandroid, error, json, json.stringify, parity
ReporterAminul Islam
AssigneeJoshua Quick
Created2018-03-23T19:24:32.000+0000
Updated2018-10-05T18:52:38.000+0000

Description

Hello ! We were trying to log some handled exceptions with the APM module for Android (apm.logHandledException( ) ) and we just noticed whenever we try to instantiate a Javascript Error in Android , this always comes out as an empty object ( { } ), where as, when doing the very same thing in iOS will work with no issues. This however, has only been tested in simulators/emulators. It was also tested with Ti SDK 7.x and 6.x. In both cases new Error seems to be broken in Android, but working in iOS. Sample app: https://propelics.box.com/s/q8nryev5c6fc8eqzl3rem21jjd792vj4
var e = new Error('Some error');
var e2 = new Error();

console.log('e=' + JSON.stringify(e));
console.log('e2=' + JSON.stringify(e2));
Steps to reproduce: 1) Take a look at index.js and see how we're trying to instantiate 2 Error objects, then printing them in the console. 2) Run the application in Android (we've been using emulators with Android 6.0 and 7.1.1) 3) When the application launches look at the console. The printed logs will be empty objects: [INFO] e={} [INFO] e2={} 4) Re-run the app in iOS 5) Observe the console output and how actual error objects will be printed: [INFO] e={"line":55,"column":19,"sourceURL":"file:///Users/user/Library/Developer/CoreSimulator/Devices/63801195-0FE5-4A3F-9615-F1ABAAB35DFC/data/Containers/Bundle/Application/D4F143DB-E591-4258-9567-6D294EBEB265/BlankApp.app/alloy/controllers/index.js"} [INFO] e2={"line":56,"column":20,"sourceURL":"file:///Users/user/Library/Developer/CoreSimulator/Devices/63801195-0FE5-4A3F-9615-F1ABAAB35DFC/data/Containers/Bundle/Application/D4F143DB-E591-4258-9567-6D294EBEB265/BlankApp.app/alloy/controllers/index.js"}

Comments

  1. Joshua Quick 2018-03-23

    The Error object is probably fine. This is likely an issue with JSON.stringify() not knowing how to enumerate the Error object's fields.
  2. Ewan Harris 2018-03-23

    For reference: V8 bug around Error properties not being enumerable https://bugs.chromium.org/p/v8/issues/detail?id=1595 Node bug for this behaviour: https://github.com/nodejs/node-v0.x-archive/issues/1634 (their fix at the time https://github.com/nodejs/node-v0.x-archive/commit/389e2a07e676e5be3b8cbd94f0a0f7ebccb47f47) Interestingly doing {var e = new Error('Some error');{Ti.API.info(e);}} gives [INFO] Error: Some error. In console.js we probably want to check if what we're passing to Ti.API. is an error and format it. I'm not sure what we can do on the Java side to print the stack as I'm unfamiliar with that code. For now if the users wants to they can access the stack directly or pass in the property names on Error to JSON.stringify, see below
       var e = new Error('Some error');
       
       // Log all properties on Error
       console.log('e=' + JSON.stringify(e, Object.getOwnPropertyNames(e)));
       // gives [INFO]  e={"stack":"Error: Some error\n    at /app.js:1:20\n    at Module._runScript (ti:/module.js:596:17)\n    at Module.load (ti:/module.js:105:7)\n    at Function.Module.runModule (ti:/module.js:74:9)","message":"Some error"}
       
       // Log only stack
       console.log('e.stack=' + e.stack);
       // gives 
       // [INFO]  e.stack=Error: Some error
       // [INFO]  at /app.js:1:20
       // [INFO]  at Module._runScript (ti:/module.js:596:17)
       // [INFO]  at Module.load (ti:/module.js:105:7)
       // [INFO]  at Function.Module.runModule (ti:/module.js:74:9)
       
  3. Christopher Williams 2018-05-14

    It sounds like this behavior is actually according to the ES5 spec in V8. If you'd like to log an Error, you could always build the string manually. The most important property is stack, which holds the message and stacktrace of the error and would likely hold all the info you'd want. They also typically have a message property which is just the message passed in (so here it'd be 'Some error').
       Ti.API.error(error.stack);
       
    We also added a nativeStack property for any under the hood java stacktrace on Android (if we had one), which will land in 7.2.0. The relevant tickets are: TIMOB-25963 and TIMOB-25965 (though I'm not sure they were updated to note the new property). So, long story short: the properties on Error are not enumerable by default on V8 due to complying more strictly to the ES5 spec, but it's relatively easy to just log the stack property if it's available, and fall back to the message property if not. But we may want to alter our JSON.stringify impl on Android to specifically handle this case.
  4. Joshua Quick 2018-06-16

    PR (master): https://github.com/appcelerator/titanium_mobile/pull/10112
  5. Lokesh Choudhary 2018-09-18

    FR Passed. JSON.stringify(Error) returns the appropriate error & not {}. Studio Ver: 5.1.1.201809051655 SDK Ver: 7.5.0 local build OS Ver: 10.13.5 Xcode Ver: Xcode 9.4.1 Appc NPM: 4.2.13 Appc CLI: 7.0.6 Daemon Ver: 1.1.3 Ti CLI Ver: 5.1.1 Alloy Ver: 1.13.2 Node Ver: 8.9.1 NPM Ver: 5.5.1 Java Ver: 10.0.2 Devices: ⇨ google Nexus 5 (Android 6.0.1) ⇨ google Nexus 6P (Android 8.1.0) Emulator: ⇨ Android 4.1.2
  6. Lokesh Choudhary 2018-09-19

    PR Merged.
  7. Keerthi Mahalingam 2018-10-05

    Verified the fix on SDK 7.5.0.v20181004095510. Error returns correctly. Closing.
       Operating System
         Name                        = Mac OS X
         Version                     = 10.13.6
         Architecture                = 64bit
       Node.js
         Node.js Version             = 8.9.1
         npm Version                 = 5.5.1
       Titanium CLI
         CLI Version                 = 5.1.1
       Titanium SDK
         SDK Version                 =7.5.0.v20181004095510
       Device			      =Oneplus 5T Android 8
       					Pixel android 6 emulator 
       

JSON Source