-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Data assets feature #54003
Comments
If we introduce something to support a featuire like We used to have We'll need some abstraction between (location of) the raw bytes and the runtime system, which allows for including the resources in the deployment unit, and which can be tree-shaken to avoid including unnecessary resources. If it's something that independent packages can use to get their own resources included in programs that use the package, then there also needs to be some kind of name-spacing — which is where using |
@lrhn : Yes, the idea is to introduce an interface which other frameworks can implement themselves, such as the already existing implementation from Flutter. The implementation to allow Regarding the |
That would be SO cool if we could tree-shake assets in flutter (flutter/flutter#64106) It probably requires to use a generated ID in the code instead of a dynamic string to reference the asset (this is also a win btw). // Use a generated id to reference assets (cfr Android way)
Image.asset(R.images.my_icon);
// instead of
Image.asset('assets/images/icon.png'); |
So what could be a solution? For example when I compile my project using |
My, very simplistic, suggestion would be: Introduce a abstract final class Resource<T> {
external const Resource(String url);
Future<T> load();
} Introduce a set of specialized resource kinds: /// A resource contanining a sequence of bytes.
abstract final class ByteResource implements Resource<Uint8List> {
external const factory BytesResource(String url);
/// The length in bytes of this resource.
Future<int> get length;
/// An unmodifiable view of the bytes of the resource.
Future<Uint8List> load();
/// An unmodifiable buffer of the bytes of the resource.
///
/// Can then be viewed as any type of typed data, like
/// ```dart
/// var doubles = (await res.loadBuffer()).asFloat64List();
/// ```
Future<ByteBuffer> loadBuffer();
/// Read the resource into an existing byte list.
Future<Uint8List> loadInto(Uint8List target, [int offset = 0]);
/// Read a range of the resource into an existing byte list.
Future<Uint8List> loadRangeInto(int start, int end, Uint8List target, [int offset = 0]);
}
/// A resource containing a Dart [String].
abstract final class StringResource implements Resource<String> {
/// A string resource, loaded from [url] and decoded using [encoding].
///
/// Encoding *must* be one of [utf8], [latin1] or [ascii].
// (TODO: Add `utf16` decoder, then also allow `utf16`, `utf16.littleEndian` and `utf16.bigEndian`.)
external const factory StringResource(String url, [Encoding encoding = utf8]);
/// Length of the string, in UTF-16 code units.
Future<int> get length;
/// Load the content of the string.
Future<String> load();
}
/// A resource containing JSON data.
abstract final class JsonResource implements Resource<Object?> {
/// The [url] must refer to a file contining JSON source, which must be UTF-8 encoded.
external const factory JsonResource(String url);
/// Read the JSON file into an unmodifiable Dart JSON value.
///
/// A JSON value is either a `List` of JSON values, a `Map` from strings to JSON values,
/// or a simple [String], [num], [bool] or `null` value.
Future<Object?> load();
} Then you specify a resource by declaring a constant: const myStringFromFile = StringResource('package:my_package/src/res/text_file.txt'); It'll be a compile-time error to use the constructor in a non- Whichever constanta are still left after compilation, the files their It's up to the runtime to decide whether to cache the file contents on first load or not, or for how long, which format the content is stored in. The one thing I'd consider is whether to support synchronous access. |
I think it is the same as |
Data asset as
|
That could be helped by having different class AssetBundle {
external Uint8List loadBytesSync(assetId);
external Future<Uint8List> loadBytes(assetId);
}
class PointerAssetBundle {
external (Pointer<Void>, int) loadBytesAsPointerSync(assetId);
external Future<(Pointer<Void>, int)> loadBytesAsPointer(assetId);
}
class FileAssetBundle {
external bool availableAsFile(assetId);
// No need for a sync and async variant.
// Non-null if `availableAsFile` returns true.
external File? asFile(assetId);
} |
I guess these bundles should then live in different places, and the ones that do not use
Side question for the |
Another possible API is: abstract interface class Asset {
const Asset(String key) = _SystemAsset;
Future<ByteBuffer> loadBytes({bool readOnly = true});
} Then you need to invoke the But that's not much different from just a top-level The
Then we will need a load-function per asset kind. I'm a little worried that the keys are just strings, but I guess the linker will complain if two assets have the same name, and any statically detectable asset access can be checked against available assets at compile-time. |
I would also like typed keys, something like an enum, but this would require codegen when adding an asset to be able to use it in the application. Having typed keys with a const constructor would help in finding usages of asset loading for tracking in I added the suggestion to go/dart-assets. |
The are namespaces per package
That is a good idea, we should do that once we have the |
Is that optional, or does the framework providing the assets enforce that the string has that form? If the latter, can you compile a file with assets, if that file doesn't have a package:URI, or is not in a package directory? (Still means that someone can access an asset of another package if they know the name. Probably no way to avoid that without some trickery.) |
That is a feature for native code assets. We want to avoid bundling two identical dynamic libraries. Also for native code, if static linking is used, all code lives in the same namespace. So trying to create encapsulation would start to create weird behavior when a different linking mode is used. I am not entirely sure if we should have the same goal with data assets or not. Maybe we should consider making an asset with id
Declaring assets in build and link hooks does enforce this. The usage from Dart does not (yet). |
That would require a Giving a warning for a constant key string is fine. Trying to enforce runtime security is not worth it. |
Problems:
translations.json
asset with keys that are actually reachable from Dart code after Dart AOT optimizations.High level outline:
AssetBundle
todart:
build.dart
(from ☂️ [vm/ffi] Native assets feature #50565) be able to output data assets besides code assets.link.dart
that runs after dart AOT compilation that can do minifying and tree-shaking for data assets.The tree-shaking info that
link.dart
would be getting is const values from arguments to static function calls and constructors:->
Some WIP format in the test cases: https://dart-review.googlesource.com/c/sdk/+/329961
More details to follow. Filing an issue so we have somewhere to point to.
The text was updated successfully, but these errors were encountered: