Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8 Enum values in objc!UIKit.d.ts are way too large #1150

Closed
NickIliev opened this issue Jun 4, 2019 · 5 comments
Closed

8 Enum values in objc!UIKit.d.ts are way too large #1150

NickIliev opened this issue Jun 4, 2019 · 5 comments
Assignees
Labels
Milestone

Comments

@NickIliev
Copy link

@bguijt commented on Mon Jun 03 2019

Please check this diff: NativeScript/NativeScript@f54f71b#diff-a4508a7cbf4cc46900ecccb75a31c03d

and find all value changes from 4294967295 (which is 0xFFFFFFFF) to 18446744073709551615 (which is 0xFFFFFFFFFFFFFFFF) which is an overflow to the JS numerical system and therefore incorrect.

This is probably OK for regular JS projects, however we use a commercial code obfuscator which is unable to handle these literals.

Please change these literals back to what they were ;-)

@NathanaelA

This comment was marked as abuse.

@bguijt
Copy link

bguijt commented Jun 4, 2019

Hi @NathanaelA,

I agree the ability to use full 64-bit integers is a big plus!

Our obfuscator is not applied to the *.d.ts files at all - it is applied to the output from webpack, in this case vendor.js, which contains this large numeric literal.

However, despite our obfuscator unable to parse this, there is still a problem: These 64-bit literals are NOT processed correctly through the Typescript compiler. For instance, the source for this enum value assigns the value 18446744073709551615 to the Any/All enum values. After compiling TS to JS, this literal is rounded to 18446744073709552000 (0x10000000000000180) which flips almost all bits to 0.

The offending compiled source is this (tns-core-modules/ui/html-view/html-view.ios.js):

function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
var html_view_common_1 = require("./html-view-common");
__export(require("./html-view-common"));
var HtmlView = (function (_super) {
    __extends(HtmlView, _super);
    function HtmlView() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    HtmlView.prototype.createNativeView = function () {
        var view = UITextView.new();
        view.scrollEnabled = false;
        view.editable = false;
        view.selectable = true;
        view.userInteractionEnabled = true;
        view.dataDetectorTypes = 18446744073709552000;
        return view;
    };
    Object.defineProperty(HtmlView.prototype, "ios", {
        get: function () {
            return this.nativeViewProtected;
        },
        enumerable: true,
        configurable: true
    });
    HtmlView.prototype.onMeasure = function (widthMeasureSpec, heightMeasureSpec) {
        var nativeView = this.nativeViewProtected;
        if (nativeView) {
            var width = html_view_common_1.layout.getMeasureSpecSize(widthMeasureSpec);
            var widthMode = html_view_common_1.layout.getMeasureSpecMode(widthMeasureSpec);
            var height = html_view_common_1.layout.getMeasureSpecSize(heightMeasureSpec);
            var heightMode = html_view_common_1.layout.getMeasureSpecMode(heightMeasureSpec);
            var desiredSize = html_view_common_1.layout.measureNativeView(nativeView, width, widthMode, height, heightMode);
            var labelWidth = widthMode === html_view_common_1.layout.AT_MOST ? Math.min(desiredSize.width, width) : desiredSize.width;
            var measureWidth = Math.max(labelWidth, this.effectiveMinWidth);
            var measureHeight = Math.max(desiredSize.height, this.effectiveMinHeight);
            var widthAndState = html_view_common_1.View.resolveSizeAndState(measureWidth, width, widthMode, 0);
            var heightAndState = html_view_common_1.View.resolveSizeAndState(measureHeight, height, heightMode, 0);
            this.setMeasuredDimension(widthAndState, heightAndState);
        }
    };
    HtmlView.prototype[html_view_common_1.htmlProperty.getDefault] = function () {
        return "";
    };
    HtmlView.prototype[html_view_common_1.htmlProperty.setNative] = function (value) {
        var _a;
        var htmlString = NSString.stringWithString(value + "");
        var nsData = htmlString.dataUsingEncoding(NSUnicodeStringEncoding);
        this.nativeViewProtected.attributedText = NSAttributedString.alloc().initWithDataOptionsDocumentAttributesError(nsData, (_a = {}, _a[NSDocumentTypeDocumentAttribute] = NSHTMLTextDocumentType, _a), null);
    };
    return HtmlView;
}(html_view_common_1.HtmlViewBase));
exports.HtmlView = HtmlView;
//# sourceMappingURL=html-view.ios.js.map

NOTE: This is not all. Further down the build, this file is bundled into vendor.js and is assigned yet another literal, which is 0x10000000000000000 (dec: 18446744073709551616, or 2^64) in this snippet:

t.prototype.createNativeView=function(){var e=UITextView.new();return e.scrollEnabled=!1,e.editable=!1,e.selectable=!0,e.userInteractionEnabled=!0,e.dataDetectorTypes=0x10000000000000000,e}

What could be the right solution to this problem? We use Typescript 3.1.1, nativescript 5.3.2, tns-core-modules 5.3.1 and tns-ios 5.2.0.
Build is running in Node v10.15.3 on macOS 10.14.5.

@mbektchiev
Copy link
Contributor

@bguijt You're right, these constants overflow the JSValues and indeed are not represented correctly. Since JSC still doesn't support BigInts, I think that the best way to fix it is to convert all constants in the .d.ts files to their signed equivalents. This way 0xFFFFFFFFFFFFFFFF will become -1 and will be correctly marshaled to native without losing precision.

@bguijt
Copy link

bguijt commented Jun 4, 2019

@mbektchiev Would Number.MAX_SAFE_INTEGER not be a safer integer?

I tried your suggested literal by adding the following snippet to my Webpack config.module.rules (which replaces all large numeric literals with 0xFFFFFFFFFFFFFFFF):

                {
                    test: /\.js$|\.ts$/,
                    loader: StringReplacePlugin.replace({
                        replacements: [
                            {
                                pattern: /0x[0-9a-fA-F]{13,100}|[0-9]{16,100}/mg,
                                replacement: (match) => {
                                    if (match != "340282346638528859811704183484516925440") {
                                        console.warn(`\x1b[1m\x1b[33mWARNING: Javascript source error: Large numeric literal ${match} found - replacing with 0xFFFFFFFFFFFFFFFF\x1b[39m\x1b[21m`);
                                        return "0xFFFFFFFFFFFFFFFF";
                                    } else {
                                        // 340282346638528859811704183484516925440.000000 is a max float value from animation.ios.js
                                        return match;
                                    }
                                }
                            }
                        ]
                    })
                }

This gives me the following console warning:

WARNING: Javascript source error: Large numeric literal 18446744073709552000 found - replacing with 0xFFFFFFFFFFFFFFFF

However, my bundledvendor.js (still) contains the literal 0x10000000000000000.

I tried with Number.MAX_SAFE_INTEGER, and this works well!

@mbektchiev
Copy link
Contributor

0xFFFFFFFFFFFFFFFF is actually the same number written as hexadecimal. To workaround the issue you can simply replace it with -1

@mbektchiev mbektchiev reopened this Jun 4, 2019
mbektchiev added a commit to NativeScript/ios-metadata-generator that referenced this issue Jun 4, 2019
This way most negative values will be correctly represented in JS and TS.
Still values between 2^53 and 2^63 will have their representation converted
to inexact `double`s because the MAX_SAFE_INTEGER is 2^53-1

refs NativeScript/ios-jsc#1150
mbektchiev added a commit that referenced this issue Jun 4, 2019
This way most negative values will be correctly represented in JS and TS.
Still values between 2^53 and 2^63 will have their representation converted
to inexact `double`s because the MAX_SAFE_INTEGER is 2^53-1

refs #1150
@mbektchiev mbektchiev self-assigned this Jun 4, 2019
@mbektchiev mbektchiev added the bug label Jun 4, 2019
@mbektchiev mbektchiev added this to the 5.4.1 milestone Jun 4, 2019
mbektchiev added a commit to NativeScript/NativeScript that referenced this issue Jun 5, 2019
`18446744073709551615` (`0xFFFFFFFFFFFFFFFF`) is an overflow to the JS
numerical system and therefore incorrect. Integer values from `-(2^53-1)` to `(2^53-1)`
only can be safely represented in JS (`Number​.MIN_SAFE_INTEGER` to
`Number​.MAX_SAFE_INTEGER`)

There's a fix in iOS Runtime's metadata generator which now automatically
converts such values to signed. (NativeScript/ios-jsc#1151)

refs NativeScript/ios-jsc#1150
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants