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

ReferenceError when trying to create a connection to device #630

Closed
2 tasks done
avielt10 opened this issue Apr 2, 2024 · 3 comments
Closed
2 tasks done

ReferenceError when trying to create a connection to device #630

avielt10 opened this issue Apr 2, 2024 · 3 comments
Labels
type: question It isn't a bug report, just a question

Comments

@avielt10
Copy link

avielt10 commented Apr 2, 2024

Issue Checklist

  • I'm using the library programmatically
  • For Scrcpy related issues, I have searched in Genymobile/scrcpy repository.

Library version

0.0.23

Environment

Windows, Chrome 123, Angular 17

Device

Android

Describe the bug

I'm using
Angular 17.3.2
TypeScript 5.4.2
node 18.19.1
running on localhost:4200

after successfully getting a device object, the connect() call throws the error below

app.component.ts:40 ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    at Object.start (wrap-readable.js:27:42)
    at new WrapReadableStream (wrap-readable.js:26:9)
    at DuplexStreamFactory.wrapReadable (duplex.js:29:16)
    at new AdbDaemonWebUsbConnection (device.js:111:33)
    at AdbDaemonWebUsbDevice.<anonymous> (device.js:245:16)
    at Generator.next (<anonymous>)
    at fulfilled (chunk-J4B6MK7R.js?v=2fd0d645:36:24)
    at _ZoneDelegate.invoke (zone.js:368:26)
    at Object.onInvoke (core.mjs:14882:33)
    at _ZoneDelegate.invoke (zone.js:367:52)

Steps to reproduce

  1. create a new Angular project
  2. connect and get an AdbDaemonWebUsbDevice object
  3. call connect()
@avielt10 avielt10 added the type:bug Something isn't working label Apr 2, 2024
@yume-chan
Copy link
Owner

yume-chan commented Apr 2, 2024

This is caused by Angular compiler.


For this code:

class Base {
  // @ts-expect-error
  constructor(callback) {
    callback();
  }
}

class Derived extends Base {
  constructor() {
    super(async () => {
      await Promise.resolve();
      console.log(this);
    });
  }
}

new Derived();

In Derived's constructor, callback is invoked before super() returns, so it can't access this synchronously. await Promise.resolve() is used to run the following code in a microtask, after super() returns.

In any modern browser with native async function support, the above code will run fine.


However, because Angular uses Zone.js to track async contexts, it must down-level compile async functions to generators. For the code above, the result is:

var Base = class {
  // @ts-expect-error
  constructor(callback) {
    callback();
  }
};
var Derived = class extends Base {
  constructor() {
    super(() => __async(this, null, function* () {
      yield Promise.resolve();
      console.log(this);
    }));
  }
};

As this is an argument to __async, it's evaluated immediately. It's not what the original code does, and violates JavaScript specs.


The offending code is:

super(
{
start: async (controller) => {
// `start` is invoked before `ReadableStream`'s constructor finish,
// so using `this` synchronously causes
// "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
// Queue a microtask to avoid this.
await Promise.resolve();
this.readable = await getWrappedReadableStream(
wrapper,
controller,
);
this.#reader = this.readable.getReader();
},


Similar TypeScript down-level compilation bug:

@yume-chan yume-chan closed this as not planned Won't fix, can't repro, duplicate, stale Apr 2, 2024
@yume-chan yume-chan added type: question It isn't a bug report, just a question and removed type:bug Something isn't working labels Apr 14, 2024
@cedricdevriendt
Copy link
Contributor

Good news! Since Angular 18 it is possible to run Angular without zone.js.

I can confirm that this library and all functionality does work on Angular 18+ with the following provider enabled: provideExperimentalZonelessChangeDetection in the application config.

More info: https://angular.dev/guide/experimental/zoneless

@yume-chan
Copy link
Owner

It should be possible to rewrite the code to work around that down-level compilation bug:

class Base {
  // @ts-expect-error
  constructor(callback) {
    callback();
  }
}

class Derived extends Base {
  constructor() {
    super(() => Promise.resolve().then(async () => { console.log(this) }));
  }
}

new Derived();

TypeScript produces this (I didn't check Angular compiler):

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
class Base {
    // @ts-expect-error
    constructor(callback) {
        callback();
    }
}
class Derived extends Base {
    constructor() {
        super(() => Promise.resolve().then(() => __awaiter(this, void 0, void 0, function* () { console.log(this); })));
    }
}
new Derived();

You can patch your node_modules if needed (there might be more places need to be patched). I don't want to add intentionally bad code for a compiler bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question It isn't a bug report, just a question
Projects
None yet
Development

No branches or pull requests

3 participants