Hacker News new | past | comments | ask | show | jobs | submit login
Math.Round opens the browser print dialog (github.com/dotnet)
274 points by gokhan on June 19, 2019 | hide | past | favorite | 61 comments



    var ASM_CONSTS = [(function(){
        var err = new Error;
        print('Stacktrace: \n');
        print(err.stack)
    } // ...
The issue is that print call. They expect it to call their own print function. But that's not in scope so it falls back on window.print (I.e. the function defined in the global object).


But this doesn't completely explain why the error's thrown?

After all, Math.round was called on a perfectly innocuous 11.1 and not some crazy NaN.


Code coverage auditing, Test error handling.


Use of a language with sane scoping rules…


I’d categorize this not as scoping but the polluted global namespace in the browser environment and JavaScript’s behavior of ignoring extra arguments.


It’s not a global `print` function that’s called but `window.print`, right? `print` is sugar for `this.print` and `this` is the window object if it’s not explicitly bound to something else. So I’d definitely say it’s a very JS-specific scoping issue.


> It’s not a global `print` function that’s called but `window.print`, right? `print` is sugar for `this.print` and `this` is the window object if it’s not explicitly bound to something else.

This is a common point of confusion because in a browser the global object is traditionally the same as window (or “self” or “frames”) so there wasn't a difference for many years until ES5 introduced strict mode, which left the default as undefined instead of silently using the global object, and things further fractured with Node (where it's “global”) and Web Workers (where only “self” works). (In browsers this is also complicated by the Window/WindowProxy distinction — see https://blog.whatwg.org/windowproxy-window-and-location and especially https://mathiasbynens.be/notes/globalthis#terminology)

A couple years back, “globalThis” was added to make it easier to write portable code across different environments without having to check various names: https://github.com/tc39/proposal-global

I don't think is not a particularly JavaScript-specific issue because any language which inherits scope can have the problem of a symbol not matching what you expected, which is why the ones which don't have compile-time checking tend to use linters which will report use of things which haven't been explicitly declared (not mention shadowing built-in names). If you want to blame JavaScript, the feature which would have made this more obvious would have been not ignoring extra arguments – print("foo") triggering an error at that point in the source would make it more obvious.


Wow, that’s pretty horrible.


> It’s not a global `print` function that’s called but `window.print`, right?

Both are true. Globals are stored on `window`.

> `print` is sugar for `this.print`

It is not.


It‘s not that print is sugar for this.print but that print is undefined and undefined variables fall back to a lookup in the global window object.


Yeah I wouldn't mind a run mode or utility where you don't have the window global and you have to explicitly import what are currently globals. It should be doable with e.g. a webpack or browser plugin.


Just don't call the method "print"


Hope that was irony.


> "But exactly which concrete bugs would a programming language and environment with a strong type system prevent?" asked the young student Master Typikos.

"These kind of errors." Master Typikos said - and she told the student the story about the time when a bug in a runtime caused Math.Round to do IO although it's type was `Integer -> Float32 -> Float32`.

The student did not believe this story. Later he was enlightened.

EDIT: refined type


How would strong typing really prevent this? This is a side effect caused by a scoping issue, calling window.print instead of the local print function.


He means Haskell-grade types (float->float is not a function touching the IO monad). It wouldn't help in any mainstream language with actual types (like C++).


It's not the Math.Round function in the source text that's doing IO. IO is done as part of the function that gets run when you click "Run"

    run : DotNetCode -> IO ()
Doing IO is expected for this function (not only might the source code to be executed do IO, but the print function that was intended to be called instead of window.print certainly would).


Assuming that the function was called with a string, but the window.print function doesn't accept a string argument, the typing systems in mainstream languages like Java (or even TypeScript) would catch this bug.


Right, that makes sense, I always thought that had more to do with the pure functional nature of Haskell than the type system, but I've never written any Haskell.


From a certain point of view that is the same thing. A pure function can only enforced using a powerful type system. Otherwise one could argue, your types are "lying".

EDIT: 's/strong/powerful'


Wow this suddenly made "get" the power of Haskell, which has escaped me for quite a while (not that I've ever actually written anything real with it). You can capture side effects _in the type system_. That is honestly brilliant.


> How would strong typing really prevent this?

calling print() would not have failed in a stronger type system.

However, calling print("foo") would resolve to window.print("foo") which should have failed because window.print takes zero arguments.


calling print() would not have failed in a stronger type system.

That depends on the type system. The notation picozeta used suggests a Haskell-style system, where you wouldn't be able to call a function that does I/O from a function that is pure, which a type like `Integer -> Float32 -> Float32` would guarantee. You would need to explicitly permit I/O with a type like `Integer -> Float32 -> IO Float32` for the Round function for the call to print to compile in this sort of system.

Edit: Although it looks like the code that calls print in this case is actually another function, which might need to be in IO anyway if you were in this sort of type system, so maybe that doesn't help here.


Answers here focus on purely functional type systems, but more generally, this type of error can be caught by effect systems https://en.m.wikipedia.org/wiki/Effect_system


You cannot “window.print” in a Float32→Float32 function. It just won’t compile.


But of course, the function in question isn’t Math.Round. It’s “interpretDotNetFunction”. Which is, necessarily, “string -> IO ()” (forgive my syntax).


You could make it compile in Haskell. You just shouldn't:

f x = unsafePerformIO $ print "boom" >> return (pi * x)


In what language?


Haskell


You can't console.log either...


There is Debug.Trace.trace which does the trick with unsafePerformIO (which is a special non-type-safe escape hatch for IO)


I love this koan but I would replace 'strong' with 'pure'.

The fact that Float32 -> Float32 does not permit IO is due to purity. 'Strong' is kind of an ambiguous term here.


True, the term "strong" is maybe too strong here. Let's go with "powerful".


No, "pure function" is an actual term: https://en.wikipedia.org/wiki/Pure_function


Yesterday's static typechecks tells us nothing about today. You can typecheck against Chrome 75 all you like, yet Chrome 76 removes/renames/modifies at its pleasure - not to mention every other browser.


Are you referencing a specific situation or debacle?


So it's caused by a reference to `print` not being what they expected.

The create-react-app project maintains a great list of "confusing browser globals" for exactly this situation! https://github.com/facebook/create-react-app/tree/master/pac...

You can use it along with ESLint to detect situations where a variable reference is technically valid (it will refer to the window property) but is probably not what you're intending.

Some of them are really easy to trip over, like `error`, `name`, and `open`. All global properties on the window!


Name got me the other day. I was writing typescript and not even targeting the browser but I guess I had browser globals in the .d.ts

I had a string like "Hello ${name}" and it would compile but crash at runtime.

Wasted a whole day, learned a lot about why even with great tools the JS ecosystem still has some fundamental crap at the bottom.


So basically a library tries to print something in the console and instead of using `console.log(...)` they use `print (...)` as if it is Python. That instead invokes `window.print` used to actually send something to the printer.


>instead of using `console.log(...)` they use `print (...)` as if it is Python.

There's a `Module.print` that wraps `console.log` with a bit of logic. The code might have intended to call that one.

    window.Module = function (e, n, f) {
      var d = {
      },
      p = [
        'DEBUGGING ENABLED'
      ];
      return d.print = function (e) {
        return p.indexOf(e) < 0 && console.log('WASM: ' + e)
      },
      d.printErr = function (e) {
        return console.error('WASM: ' + e)
      },
      ...
      d
    }(e, n, f)


This happens because of scope chaining in JavaScript. If you call a function it will look for it in the current context. If it’s not there it will try to find it in the parent. If it’s not there it will keep checking the parent objects until it finds it, or throw an exception. Since window is the top level context and includes a print function, it gets called and shows the printer dialog.


Not the parent objects – the containing scopes. (Which is how scope works in most languages.)


I wish the world could decide on a standard console method. I, far too often, catch myself typing console. in Java. It's even worse if I've done some C# recently.


I agree with https://github.com/dotnet/try/issues/290#issuecomment-503768... - this seems pretty likely to be "you are using `print(...)` but don't have a custom one in scope, so it's calling the window's func"

It happens in Firefox too, fwiw. Probably just a mono.js flaw.


In the process of trying to reproduce this bug in some rather silly environments, I've instead discovered that try.dot.net is completely broken on multiple browsers:

- On Firefox for Android, it's impossible to type in a program, since when typing something as simple as "using System;" it'll garble the text like crazy

- On Android browsers in general, the Monaco editor on that page is too "smart" for its own good and presents a worthless context menu when I press and hold (i.e. to select all text or paste it in). Microsoft: could y'all not?

- On both the (presumably-Chromium-based) default browser on my Android phone and Firefox 52.9.0 ESR on a reasonably "recent" OpenIndiana, the Blazor runtime fails to run anything at all when I click "Run", complaining in the JS console (at least on OpenIndiana; didn't check on my phone, but the symptoms are the same) that "No .NET call dispatcher has been set".

- On a reasonably-recent nightly version of Haiku, loading try.dot.net at all causes WebPositive to outright crash within seconds.


But why exactly are so many people determined to try to reproduce this particular bug in every permutation of device/os/browser known to man?


They’re joining a “LOL JavaScript” but refusing to use the easy tools available to understand what’s actually happening, so there’s lots of shooting in the dark – not unlike the thread here about strong type systems.


Why not?


Microsoft explicitly states[0] that they do not support Monaco on mobile browsers, since it's just too much hassle to maintain and, frankly, nobody codes on mobile.

[0]: https://microsoft.github.io/monaco-editor/


My point is more that Microsoft's trying to do too much and make Monaco too fancy. Monaco breaks in amusing ways on desktop browsers, too (prime example: none of the sample editors on https://microsoft.github.io/monaco-editor/ are scrollable with the mouse wheel on Firefox on Linux; also, the context menu continues to lack either "select all" or "paste").

If Monaco wasn't so absurdly over-engineered it would be much easier to support on mobile browsers.


Why does `print(...)` in Firefox acts like `console.log`?


It only acts like console.log in the browser console, not in a script. Interesting.


The Chrome console defines a few console-only convenience functions too: https://github.com/GoogleChrome/devtools-docs/blob/master/do...


Handy for debugging I guess?


Curious if there are any browsers and circumstances where instead of the dialog window.print() would immediately print something.


And here I thought the story was about the endless stream of “me too” comments in the bug report. Hard to find much signal in there.


  C:\>copy WEB LPT1


What is the expected behavior from calling print()?


The JavaScript function "window.print()" is expected to open a print dialog for the current document. Since "window" is also the global object for the main/UI thread in a browser, "print()" resolves to "window.print()", if no function has been defined otherwise. This is, BTW, also a potential issue in Emscripten, which uses a similar logging mechanism by a custom "print()" function. (I guess, this is also, where it came from in the WASM implementation. A few years ago, I actually replaced all references to "print" by a more safe name for a customized Emscripten runtime, in order to prevent similar from happening in any edge cases. I suggest to treat "print" and similar global function names just as if they were reserved words in your projects to avoid "expectable unexpecteds" like this. For similar reasons, using a variable "self" to store a reference to the current this-object isn't a great idea, since "self" is already the canonical reference to the global object…)

Edit: To be more specific, "window" is only the global object for the scripting context in a browser window (anything inside "script" tags). Meaning, you would have to use the full form, "window.print()", when calling the print function from outside this context, like inside an event handler attribute of an HTML tag. (E.g., '<button onclick="window.print()">Print page<button>'. Here, a simple "print()" wouldn't work.)


A globally defined print function is invoked I guess? It's weird for me to see it in the raw like that instead of this.print or errorController.print or this.emit('error', error) or just throw(error)...


Calling print() opens a print dialog in Javascript. The error-handling code in this project is doing the wrong thing. It's not an example of a wild Javascript-specific error, just an example of untested error handling code doing the wrong thing.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: