ethanblake4 / dart_eval

Extensible Dart interpreter for Dart with full interop
https://pub.dev/packages/dart_eval
BSD 3-Clause "New" or "Revised" License
334 stars 40 forks source link

[Question] is it possible to have a eval class extend another eval class and override few methods #138

Open Noobware1 opened 1 year ago

Noobware1 commented 1 year ago

By extending another eval class I mean a eval class which already extends a bridge class.

I only want my eval class to extend this class and only override two of it's methods not all of them.

I tried doing it but received a null check operator used on a null value error.

ethanblake4 commented 1 year ago

It should be possible, it's a bug if not. Can you provide a test case?

Noobware1 commented 11 months ago

got an error when I tried doing this

// ignore_for_file: unnecessary_this

import 'mega_cloud.dart';
import 'package:meiyou_extenstions/meiyou_extenstions.dart';

class RabbitStream extends MegaCloud {
  RabbitStream(ExtractorLink extractorLink) : super(extractorLink);

  @override
  String getUrl() {
    final serverUrl = this.extractorLink.url;
    final embed = RegExp(r'embed-\d').firstMatch(serverUrl)?.group(0);
    final id = StringUtils.substringBefore(
        StringUtils.substringAfterLast(serverUrl, "/"), "?");

    return '$hostUrl/ajax/$embed/getSources?id=$id';
  }

  @override
  String get keyUrl =>
      'https://raw.githubusercontent.com/Noobware1/zoro-keys/e4/key';
}

mega_cloud.dart

// ignore_for_file: unnecessary_this

import 'dart:convert';

import 'package:meiyou_extenstions/meiyou_extenstions.dart';

// void main(List<String> args) {
//   MegaCloud(ExtractorLink(
//           url: 'https://megacloud.tv/embed-2/e-1/4moscC8xgnRg?k=1', name: ''))
//       .extract()
//       .then(print);
// }

class MegaCloud extends ExtractorApi {
  MegaCloud(ExtractorLink extractorLink) : super(extractorLink);

  String get hostUrl =>
      StringUtils.substringBefore(this.extractorLink.url, '/embed');

  String getUrl() {
    final serverUrl = this.extractorLink.url;
    final embed = RegExp(r'embed-\d').firstMatch(serverUrl)?.group(0);
    final id = StringUtils.substringBefore(
        StringUtils.substringAfterLast(serverUrl, "/"), "?");

    final e = RegExp(r'e-\d').firstMatch(serverUrl)?.group(0) ?? '1';
    return '$hostUrl/$embed/ajax/$e/getSources?id=$id';
  }

  @override
  Future<Video> extract() async {
    final jsonLink = getUrl();

    final response =
        (await AppUtils.httpRequest(url: jsonLink, method: 'GET', headers: {
      'X-Requested-With': 'XMLHttpRequest',
      'Referer': this.extractorLink.url,
      'User-Agent':
          'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0',
    }))
            .json();

    final List<VideoSource> sources;

    if (response['encrypted'] == false) {
      sources = ListUtils.mapList(
          (response['sources'] as List), (e) => toVideoSource(e));
    } else {
      final decryptKey = await getDecryptKey();
      print(decryptKey);

      List<String> sourcesArray =
          StringUtils.valueToString(response['sources']).split('');

      var extractedKey = '';
      var currentIndex = 0;
      for (var index in decryptKey) {
        var start = index[0] + currentIndex;
        var end = start + index[1];

        for (var i = start; i < end; i++) {
          extractedKey += sourcesArray[i];
          sourcesArray[i] = '';
        }

        currentIndex += index[1];
      }
      final decrypted = CryptoUtils.AES(
          ciphertext: sourcesArray.join(''), key: extractedKey, encrypt: false);

      sources = ListUtils.mapList(
          (json.decode(decrypted) as List), (e) => toVideoSource(e));
    }
    final List<Subtitle>? tracks;

    if (response['tracks'] == null) {
      tracks = null;
    } else {
      tracks = ListUtils.mapList((response['tracks'] as List), (e) {
        return toSubtitle(e);
      }).where((e) => removeThumbnail(e)).toList();
    }

    return Video(
      videoSources: sources,
      subtitles: tracks,
    );
  }

  String get keyUrl =>
      'https://raw.githubusercontent.com/Noobware1/zoro-keys/e1/key';

  Future<List<List<int>>> getDecryptKey() async {
    return (await AppUtils.httpRequest(url: keyUrl, method: 'GET'))
        .json<List<List<int>>>((json) {
      return ListUtils.mapList(
        json as List,
        (l) => ListUtils.mapList(
            l, (l) => StringUtils.toInt(StringUtils.valueToString(l))),
      );
    });
  }

  VideoSource toVideoSource(dynamic e) {
    return VideoSource(
      url: e['file'],
      quality: VideoQuality.hlsMaster,
      format: getVideoFormat(StringUtils.valueToString(e['type'])),
    );
  }

  bool removeThumbnail(Subtitle e) {
    return e.langauge != 'thumbnails';
  }

  VideoFormat getVideoFormat(String type) {
    if (type == 'hls') {
      return VideoFormat.hls;
    }
    return VideoFormat.mp4;
  }

  Subtitle toSubtitle(dynamic e) {
    return Subtitle(
      url: e['file'],
      langauge: e['label'] ?? 'thumbnails',
      format: AppUtils.getSubtitleFromatFromUrl(e['file']),
    );
  }

  @override
  String get name => 'MegaCloud';
}
Noobware1 commented 11 months ago

it should return $ExtractorApi but it returns $InstanceImpl here's the error

Instance of '$InstanceImpl'
Unhandled exception:
NoSuchMethodError: Class '$InstanceImpl' has no instance method 'extract'.
Receiver: Instance of '$InstanceImpl'
Tried calling: extract()
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
#1      main (file:///C:/Users/freem/OneDrive/Desktop/Projects/meiyou_extensions_repo/bin/test.dart:45:22)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:294:33)
#3      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:189:12)
Noobware1 commented 6 months ago

@ethanblake4 I actually want to fix it can you telling me where should I start?

ethanblake4 commented 6 months ago

Sure. Let's say you have class A extends B {} and then you create a new A().

If both class A and B are not bridge classes, the way this works is conceptually pretty simple (pseudocode):

final inst_b = new_class(B);
final inst_a = new_class(A, super: inst_b);
return inst_a;

However, if class B is a bridge class, the logic is completely different:

final inst_shim = new_bridge_super_shim();
final inst_a = new_class(A, super: inst_shim);
final inst_b = new_bridge_class(B);
inst_b.bridge_data.subclass = inst_a;
inst_shim.parent = inst_b;
return inst_b;

The code that actually does this is a little more complicated and is mostly in constructor.dart. You can also read more details about this here. However, the key thing to notice is that even though we're supposed to be creating a new A, we're actually returning the class B instance instead. That works because bridge classes automatically delegate to their assigned subclass before calling their own methods, and is required so that we can return the correct type (like in your case, returning $ExtractorApi instead of $InstanceImpl).

The problem is that dart_eval currently only does that if the direct superclass is a bridge class, but it should actually do it no matter how far back in the chain the superclass is. Eg if you have ClassA which extends ClassB which extends ClassC which extends SomeBridgeClass, it should still return the instance of SomeBridgeClass even when you create a new ClassA(). Additionally, the superclass of ClassC will have to be the bridge super shim in order for calls to super to work, but the superclass of ClassA should be ClassB (and the superclass of ClassB should be ClassC) just as normal.

Pseudocode:

final inst_shim = new_bridge_super_shim();
final inst_c = new_class(ClassC, super: inst_shim);
final inst_b = new_class(ClassB, super: inst_c);
final inst_a = new_class(ClassA, super: inst_b);
final inst_bridge_class = new_bridge_class(SomeBridgeClass);
inst_bridge_class.bridge_data.subclass = inst_a;
inst_shim.parent = inst_bridge_class;
return inst_bridge_class;
Noobware1 commented 5 months ago

I only need modify constructor.dart or do I need to modify runtime ops or other files related to bridge too?