4

Assume the following piece of code, which runs fine

var Loader = { // Can be 'const' as well 
  get db() {
    throw('exception');
  }
}
function test(){
   console.log("I'm fine")
}

test()

And almost the same, with var omitted, which is also fine(in javascript)

Loader = { // Can be 'const' as well 
  get db() {
    throw('exception');
  }
}
function test(){
   console.log("I'm fine")
}

test()

However, if second one is run inside Google Apps Script(assuming you run function test) it fails on exception, trying to execute throw('exception') from getter of 'db' property. Why is that?

Error: https://i.stack.imgur.com/9qlQx.jpg

Sample script

roma
  • 1,512
  • 10
  • 20
  • Possible bug in their transpilation step? – Ben Aston Oct 23 '20 at 22:35
  • 2
    It assigns (creates) a global variable. It would fail in strict mode. I'll have to assume something in the environment accesses `Loader.db`. Does it happen with all variable names? – Bergi Oct 24 '20 at 02:31
  • @Bergi, all of the code used is in snippet and/or provided sample script. Whatever happens is actual only for GAS(same works for javascript), so I'm really curious what can trigger initialization of getter for object which is never accessed. – roma Oct 25 '20 at 08:04
  • @TheMaster I never added "javascript" to tags cause question is related to GAS implementation internals, I don't think anyone with just javascript knowledge can answer this. – roma Oct 25 '20 at 08:13
  • 2
    I'm sure js experts have deep insights into how each `getter` operates and that's why I added the tag. Except what Bergi postulated( *I'll have to assume something in the environment accesses Loader.db* ), I don't know why `getter` executes, when declared without `var`. Feel free to rollback the edit, if you feel JavaScript tag is irrelevant. In this case, you might wanna create a bug report in the issuetracker and add the link as a answer. See [tag info page](https://stackoverflow.com/tags/google-apps-script/info) for more details. – TheMaster Oct 25 '20 at 09:30
  • Btw, the "can be a const" is a bit misleading. I tested the same code with `var`, `let`, `const` (basically, with keyword declaration), and the only way it happens is when the declaration is global (obviously nothing happens when "using" block scope). Something *must* be invoking the getter during parsing. My suspicion is on the part that populates the global object with services - maybe the implementation goes over the top-level object properties declared with global binding, but I am not sure why would there be a difference in declaring *in the global context* with `var` or without. – Oleg Valter is with Ukraine Oct 26 '20 at 14:03
  • 1
    @TheMaster - I also went ahead and added V8 and [global-variables] tags as Rhino runtime does not have this problem (verified in my playground). And that makes me more and more convinced that the reason lies in how the global context is populated with built-in services, as we know that in Rhino the user code is placed into an interim special context – Oleg Valter is with Ukraine Oct 26 '20 at 14:20
  • I am not able to reproduce this in V8. Running the second snippet's `test()` function just logs "I'm fine" in the console. No exception thrown. – Aerials Oct 29 '20 at 15:27
  • 1
    @Aerials That's very interesting. I was able to reproduce it. So, I checked again after your comment: Thing is, if `test()`(the last line-function call to `test`) is present, this issue is not reproducible(or masked? Not sure)- Here two calls to `test` are made-one from script and one from user interface. If you remove the last line and call `test` directly only from the user interface, the issue is reproducible. – TheMaster Oct 29 '20 at 15:48
  • @TheMaster you are correct. – Aerials Oct 29 '20 at 16:00
  • @Aerials - that is a very interesting observation, btw, the presence of the call of a function should not affect how the globally declared variable is accessed. TheMaster - seems like it is not masked, just does not occur (if we try to log anything before throwing the exception, nothing happens), which is puzzling. – Oleg Valter is with Ukraine Oct 30 '20 at 09:55

1 Answers1

4

From a pure JavaScript point of view, the difference between global variables created using the var keyword or using no keyword at all is the configurability bit of the property that's created on the global object:

foo = 1;
Object.getOwnPropertyDescriptor(this, "foo")
// {value: 1, writable: true, enumerable: true, configurable: true}

var bar = 2;
Object.getOwnPropertyDescriptor(this, "bar")
// {value: 2, writable: true, enumerable: true, configurable: false}

So in most cases, you wouldn't notice that difference, but it is detectable if you insist on finding it.

Now, why this affects the behavior of GAS, I have no idea. Looks like some part of that framework iterates over all configurable properties of the global object and their sub-properties. I'm not familiar with GAS so I don't know why it might want/have to do that. If it matters to you, maybe file a bug to ask?

Does it happen with all variable names? --Bergi

Yes, it happens for Loader.db just as it does for MyLoader.mydb, so it's not just an unlucky collision with one particular built-in thing.

jmrk
  • 34,271
  • 7
  • 59
  • 74