diff --git a/assets/directory_asset_bundle.h b/assets/directory_asset_bundle.h index 0bbe5dc3918f5..75843ea4cd685 100644 --- a/assets/directory_asset_bundle.h +++ b/assets/directory_asset_bundle.h @@ -19,9 +19,9 @@ class DirectoryAssetBundle { bool GetAsBuffer(const std::string& asset_name, std::vector* data); - private: std::string GetPathForAsset(const std::string& asset_name); + private: const std::string directory_; FXL_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle); diff --git a/runtime/dart_init.cc b/runtime/dart_init.cc index db4b093149904..2608b8977f4eb 100644 --- a/runtime/dart_init.cc +++ b/runtime/dart_init.cc @@ -14,6 +14,7 @@ #include #include +#include "flutter/assets/directory_asset_bundle.h" #include "flutter/assets/unzipper_provider.h" #include "flutter/assets/zip_asset_store.h" #include "flutter/common/settings.h" @@ -27,6 +28,7 @@ #include "flutter/runtime/start_up.h" #include "lib/fxl/arraysize.h" #include "lib/fxl/build_config.h" +#include "lib/fxl/files/path.h" #include "lib/fxl/logging.h" #include "lib/fxl/time/time_delta.h" #include "lib/tonic/converter/dart_converter.h" @@ -265,6 +267,16 @@ Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, #endif // FLUTTER_RUNTIME_MODE } +static bool GetAssetAsBuffer( + const std::string& name, + std::vector* data, + std::unique_ptr& directory_asset_bundle, + fxl::RefPtr& asset_store) { + return (directory_asset_bundle && + directory_asset_bundle->GetAsBuffer(name, data)) || + (asset_store && asset_store->GetAsBuffer(name, data)); +} + Dart_Isolate IsolateCreateCallback(const char* script_uri, const char* main, const char* package_root, @@ -296,11 +308,30 @@ Dart_Isolate IsolateCreateCallback(const char* script_uri, if (!running_from_source) { // Attempt to copy the snapshot from the asset bundle. const std::string& bundle_path = entry_path; - fxl::RefPtr zip_asset_store = - fxl::MakeRefCounted( - GetUnzipperProviderForPath(std::move(bundle_path))); - zip_asset_store->GetAsBuffer(kKernelAssetKey, &kernel_data); - zip_asset_store->GetAsBuffer(kSnapshotAssetKey, &snapshot_data); + + struct stat stat_result = {}; + if (::stat(bundle_path.c_str(), &stat_result) == 0) { + std::unique_ptr directory_asset_bundle; + // TODO(zarah): Remove usage of zip_asset_store once app.flx is removed. + fxl::RefPtr zip_asset_store; + // bundle_path is either the path to app.flx or the flutter assets + // directory. + std::string flx_path = bundle_path; + if (S_ISDIR(stat_result.st_mode)) { + directory_asset_bundle = + std::make_unique(bundle_path); + flx_path = files::GetDirectoryName(bundle_path) + "/app.flx"; + } + + if (access(flx_path.c_str(), R_OK) == 0) { + zip_asset_store = fxl::MakeRefCounted( + GetUnzipperProviderForPath(flx_path)); + } + GetAssetAsBuffer(kKernelAssetKey, &kernel_data, directory_asset_bundle, + zip_asset_store); + GetAssetAsBuffer(kSnapshotAssetKey, &snapshot_data, + directory_asset_bundle, zip_asset_store); + } } } @@ -561,9 +592,10 @@ void InitDartVM(const uint8_t* vm_snapshot_data, #endif if (!bundle_path.empty()) { - auto zip_asset_store = fxl::MakeRefCounted( - GetUnzipperProviderForPath(std::move(bundle_path))); - zip_asset_store->GetAsBuffer(kPlatformKernelAssetKey, &platform_data); + std::unique_ptr directory_asset_bundle = + std::make_unique(bundle_path); + directory_asset_bundle->GetAsBuffer(kPlatformKernelAssetKey, + &platform_data); if (!platform_data.empty()) { kernel_platform = Dart_ReadKernelBinary( platform_data.data(), platform_data.size(), ReleaseFetchedBytes); diff --git a/runtime/dart_init.h b/runtime/dart_init.h index 62c69c6a3917b..99c8fe89137e3 100644 --- a/runtime/dart_init.h +++ b/runtime/dart_init.h @@ -15,13 +15,13 @@ namespace blink { -// Name of the kernel blob asset within the FLX bundle. +// Name of the kernel blob asset within the asset directory. extern const char kKernelAssetKey[]; -// Name of the snapshot blob asset within the FLX bundle. +// Name of the snapshot blob asset within the asset directory. extern const char kSnapshotAssetKey[]; -// Name of the platform kernel blob asset within the FLX bundle. +// Name of the platform kernel blob asset within the asset directory. extern const char kPlatformKernelAssetKey[]; bool IsRunningPrecompiledCode(); diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 6bb5977ac13bc..b72090db02ef1 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -201,9 +201,17 @@ void Engine::Init(const std::string& bundle_path) { #error Unknown OS #endif + std::string flx_path = bundle_path; + struct stat stat_result = {}; + if (::stat(flx_path.c_str(), &stat_result) == 0) { + if (S_ISDIR(stat_result.st_mode)) { + flx_path = files::GetDirectoryName(bundle_path) + "/app.flx"; + } + } + blink::InitRuntime(vm_snapshot_data, vm_snapshot_instr, default_isolate_snapshot_data, - default_isolate_snapshot_instr, bundle_path); + default_isolate_snapshot_instr, flx_path); } const std::string Engine::main_entrypoint_ = "main"; @@ -485,18 +493,18 @@ void Engine::ConfigureAssetBundle(const std::string& path) { return; } + std::string flx_path; if (S_ISDIR(stat_result.st_mode)) { directory_asset_bundle_ = std::make_unique(path); - return; + flx_path = files::GetDirectoryName(path) + "/app.flx"; + } else if (S_ISREG(stat_result.st_mode)) { + flx_path = path; } - if (S_ISREG(stat_result.st_mode)) { + if (PathExists(flx_path)) { asset_store_ = fxl::MakeRefCounted( - blink::GetUnzipperProviderForPath(path)); - directory_asset_bundle_ = std::make_unique( - files::GetDirectoryName(path)); - return; + blink::GetUnzipperProviderForPath(flx_path)); } } @@ -595,12 +603,9 @@ void Engine::HandleAssetPlatformMessage( const auto& data = message->data(); std::string asset_name(reinterpret_cast(data.data()), data.size()); - std::vector asset_data; - if (GetAssetAsBuffer(asset_name, &asset_data)) { - response->Complete(std::move(asset_data)); - } else { - response->CompleteEmpty(); - } + std::string asset_path = directory_asset_bundle_->GetPathForAsset(asset_name); + response->Complete( + std::vector(asset_path.begin(), asset_path.end())); } bool Engine::GetAssetAsBuffer(const std::string& name, diff --git a/shell/common/engine.h b/shell/common/engine.h index 6f7eb7c087989..60d9944c67211 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -116,7 +116,7 @@ class Engine : public blink::RuntimeDelegate { std::string country_code_; std::string user_settings_data_; bool semantics_enabled_ = false; - // TODO(abarth): Unify these two behind a common interface. + // TODO(zarah): Remove usage of asset_store_ once app.flx is removed. fxl::RefPtr asset_store_; std::unique_ptr directory_asset_bundle_; // TODO(eseidel): This should move into an AnimatorStateMachine. diff --git a/shell/common/switches.h b/shell/common/switches.h index 2432523dd46ae..6d5bd03a80a72 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -62,6 +62,9 @@ DEF_SWITCH(EnableTxt, "enable-txt", "Enable libtxt as the text shaping library instead of Blink.") DEF_SWITCH(FLX, "flx", "Specify the FLX path.") +DEF_SWITCH(FlutterAssetsDir, + "flutter-assets-dir", + "Path to the Flutter assets directory.") DEF_SWITCH(Help, "help", "Display this help text.") DEF_SWITCH(LogTag, "log-tag", "Tag associated with log messages.") DEF_SWITCH(MainDartFile, "dart-main", "The path to the main Dart file.") diff --git a/shell/platform/android/io/flutter/view/FlutterMain.java b/shell/platform/android/io/flutter/view/FlutterMain.java index 20a2121189172..090f2820bb47a 100644 --- a/shell/platform/android/io/flutter/view/FlutterMain.java +++ b/shell/platform/android/io/flutter/view/FlutterMain.java @@ -38,6 +38,7 @@ public class FlutterMain { private static final String AOT_ISOLATE_SNAPSHOT_INSTR_KEY = "isolate-snapshot-instr"; private static final String FLX_KEY = "flx"; private static final String SNAPSHOT_BLOB_KEY = "snapshot-blob"; + private static final String FLUTTER_ASSETS_DIR_KEY = "flutter-assets-dir"; // XML Attribute keys supported in AndroidManifest.xml public static final String PUBLIC_AOT_AOT_SHARED_LIBRARY_PATH = @@ -54,6 +55,8 @@ public class FlutterMain { FlutterMain.class.getName() + '.' + FLX_KEY; public static final String PUBLIC_SNAPSHOT_BLOB_KEY = FlutterMain.class.getName() + '.' + SNAPSHOT_BLOB_KEY; + public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY = + FlutterMain.class.getName() + '.' + FLUTTER_ASSETS_DIR_KEY; // Resource names used for components of the precompiled snapshot. private static final String DEFAULT_AOT_SHARED_LIBRARY_PATH= "app.so"; @@ -63,6 +66,7 @@ public class FlutterMain { private static final String DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR = "isolate_snapshot_instr"; private static final String DEFAULT_FLX = "app.flx"; private static final String DEFAULT_SNAPSHOT_BLOB = "snapshot_blob.bin"; + private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets"; private static final String MANIFEST = "flutter.yaml"; @@ -79,6 +83,7 @@ public class FlutterMain { private static String sAotIsolateSnapshotInstr = DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR; private static String sFlx = DEFAULT_FLX; private static String sSnapshotBlob = DEFAULT_SNAPSHOT_BLOB; + private static String sFlutterAssetsDir = DEFAULT_FLUTTER_ASSETS_DIR; private static boolean sInitialized = false; private static ResourceExtractor sResourceExtractor; @@ -239,6 +244,7 @@ private static void initResources(Context applicationContext) { sResourceExtractor = new ResourceExtractor(context) .addResources(SKY_RESOURCES) .addResource(sFlx); + .addResource(sFlutterAssetsDir) if (sIsPrecompiledAsSharedLibrary) { sResourceExtractor .addResource(sAotSharedLibraryPath); @@ -290,7 +296,7 @@ public static boolean isRunningPrecompiledCode() { public static String findAppBundlePath(Context applicationContext) { String dataDirectory = PathUtils.getDataDirectory(applicationContext); - File appBundle = new File(dataDirectory, sFlx); + File appBundle = new File(dataDirectory, sFlutterAssetsDir); return appBundle.exists() ? appBundle.getPath() : null; } } diff --git a/shell/platform/android/io/flutter/view/ResourceExtractor.java b/shell/platform/android/io/flutter/view/ResourceExtractor.java index d85197a8fa7f0..0975e089cb273 100644 --- a/shell/platform/android/io/flutter/view/ResourceExtractor.java +++ b/shell/platform/android/io/flutter/view/ResourceExtractor.java @@ -17,8 +17,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedList; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -48,37 +50,40 @@ private void extractResources() { try { byte[] buffer = null; final String[] assets = manager.list(""); - for (String asset : assets) { + LinkedList assetList = new LinkedList<>(Arrays.asList(assets)); + while(!assetList.isEmpty()) { + String asset = assetList.pop(); if (!mResources.contains(asset)) continue; + + if (manager.list(asset).length > 0) { + // The asset is a directory + for (String a: manager.list(asset)) { + assetList.add(asset + File.separator + a); + mResources.add(asset + File.separator + a); + } + continue; + } + final File output = new File(dataDir, asset); if (output.exists()) continue; - InputStream is = null; - OutputStream os = null; - try { - is = manager.open(asset); - os = new FileOutputStream(output); - if (buffer == null) { - buffer = new byte[BUFFER_SIZE]; - } - - int count = 0; - while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { - os.write(buffer, 0, count); - } - os.flush(); - } finally { - try { - if (is != null) { - is.close(); - } - } finally { - if (os != null) { - os.close(); - } - } + if (output.getParentFile() != null) { + output.getParentFile().mkdirs(); } + try (InputStream is = manager.open(asset)) { + try (OutputStream os = new FileOutputStream(output)) { + if (buffer == null) { + buffer = new byte[BUFFER_SIZE]; + } + + int count = 0; + while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { + os.write(buffer, 0, count); + } + os.flush(); + } + } } } catch (IOException e) { Log.w(TAG, "Exception unpacking resources: " + e.getMessage()); @@ -179,10 +184,18 @@ public boolean accept(File dir, String name) { private void deleteFiles() { final File dataDir = new File(PathUtils.getDataDirectory(mContext)); - for (String resource : mResources) { + LinkedList files = new LinkedList<>(mResources); + while (!files.isEmpty()) { + String resource = files.pop(); final File file = new File(dataDir, resource); if (file.exists()) { - file.delete(); + if (file.isFile()) { + file.delete(); + } else { + for (String f : file.list()) { + files.add(resource + File.separator + f); + } + } } } for (String timestamp : getExistingTimestamps(dataDir)) { diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h b/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h index 79c7a7a1c9a9a..615905bec931d 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h @@ -20,6 +20,13 @@ FLUTTER_EXPORT - (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)archiveURL NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFlutterAssets:(NSURL*)archiveURL + dartMain:(NSURL*)dartMainURL + packages:(NSURL*)dartPackages NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)archiveURL + NS_DESIGNATED_INITIALIZER; + - (instancetype)initFromDefaultSourceForConfiguration; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index e220b326ad063..60ba7b81aa77f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -43,7 +43,7 @@ + (void)initialize { #pragma mark - Override base class designated initializers - (instancetype)init { - return [self initWithFLXArchive:nil dartMain:nil packages:nil]; + return [self initWithFlutterAssets:nil dartMain:nil packages:nil]; } #pragma mark - Designated initializers @@ -63,12 +63,22 @@ - (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle { - (instancetype)initWithFLXArchive:(NSURL*)archiveURL dartMain:(NSURL*)dartMainURL packages:(NSURL*)dartPackages { + return nil; +} + +- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)archiveURL { + return nil; +} + +- (instancetype)initWithFlutterAssets:(NSURL*)flutterAssetsURL + dartMain:(NSURL*)dartMainURL + packages:(NSURL*)dartPackages { self = [super init]; if (self) { _dartSource = [[FlutterDartSource alloc] initWithDartMain:dartMainURL packages:dartPackages - flxArchive:archiveURL]; + flutterAssets:flutterAssetsURL]; [self checkReadiness]; } @@ -76,11 +86,12 @@ - (instancetype)initWithFLXArchive:(NSURL*)archiveURL return self; } -- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)archiveURL { +- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL { self = [super init]; if (self) { - _dartSource = [[FlutterDartSource alloc] initWithFLXArchiveWithScriptSnapshot:archiveURL]; + _dartSource = + [[FlutterDartSource alloc] initWithFlutterAssetsWithScriptSnapshot:flutterAssetsURL]; [self checkReadiness]; } @@ -100,19 +111,19 @@ - (instancetype)initFromDefaultSourceForConfiguration { // Load directly from sources if the appropriate command line flags are // specified. If not, try loading from a script snapshot in the framework // bundle. - NSURL* flxURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::FLX)); + NSURL* flutterAssetsURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::FlutterAssetsDir)); - if (flxURL == nil) { + if (flutterAssetsURL == nil) { // If the URL was not specified on the command line, look inside the // FlutterApplication bundle. - NSString* flxPath = [self pathForFLXFromBundle:bundle]; - if (flxPath != nil) { - flxURL = [NSURL fileURLWithPath:flxPath isDirectory:NO]; + NSString* flutterAssetsPath = [self pathForFlutterAssetsFromBundle:bundle]; + if (flutterAssetsPath != nil) { + flutterAssetsURL = [NSURL fileURLWithPath:flutterAssetsPath isDirectory:NO]; } } - if (flxURL == nil) { - NSLog(@"Error: FLX file not present in bundle; unable to start app."); + if (flutterAssetsURL == nil) { + NSLog(@"Error: flutterAssets directory not present in bundle; unable to start app."); [self release]; return nil; } @@ -120,7 +131,8 @@ - (instancetype)initFromDefaultSourceForConfiguration { NSURL* dartMainURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::MainDartFile)); NSURL* dartPackagesURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::Packages)); - return [self initWithFLXArchive:flxURL dartMain:dartMainURL packages:dartPackagesURL]; + return + [self initWithFlutterAssets:flutterAssetsURL dartMain:dartMainURL packages:dartPackagesURL]; } NSAssert(NO, @"Unreachable"); @@ -142,14 +154,14 @@ - (void)checkReadiness { } } -- (NSString*)pathForFLXFromBundle:(NSBundle*)bundle { - NSString* flxName = [bundle objectForInfoDictionaryKey:@"FLTFlxName"]; - if (flxName == nil) { - // Default to "app.flx" - flxName = @"app"; +- (NSString*)pathForFlutterAssetsFromBundle:(NSBundle*)bundle { + NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"]; + if (flutterAssetsName == nil) { + // Default to "flutter_assets" + flutterAssetsName = @"flutter_assets"; } - return [bundle pathForResource:flxName ofType:@"flx"]; + return [bundle pathForResource:flutterAssetsName ofType:nil]; } #pragma mark - Launching the project in a preconfigured engine. @@ -257,10 +269,10 @@ - (void)runFromPrecompiledSourceInEngine:(shell::Engine*)engine return; } - NSString* path = [self pathForFLXFromBundle:_precompiledDartBundle]; + NSString* path = [self pathForFlutterAssetsFromBundle:_precompiledDartBundle]; if (path.length == 0) { NSString* message = [NSString stringWithFormat: - @"Could not find the 'app.flx' archive in " + @"Could not find the 'flutter_assets' dir in " @"the precompiled Dart bundle with ID '%@'", _precompiledDartBundle.bundleIdentifier]; result(NO, message); @@ -293,7 +305,7 @@ - (void)runFromSourceInEngine:(shell::Engine*)engine return result(NO, message); } - std::string bundle_path = _dartSource.flxArchive.absoluteURL.path.UTF8String; + std::string bundle_path = _dartSource.flutterAssets.absoluteURL.path.UTF8String; if (_dartSource.archiveContainsScriptSnapshot) { blink::Threads::UI()->PostTask([ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h b/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h index 8cdfc4ab707c2..34ab52931bf81 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h @@ -13,14 +13,15 @@ typedef void (^ValidationResult)(BOOL result, NSString* message); @property(nonatomic, readonly) NSURL* dartMain; @property(nonatomic, readonly) NSURL* packages; -@property(nonatomic, readonly) NSURL* flxArchive; +@property(nonatomic, readonly) NSURL* flutterAssets; @property(nonatomic, readonly) BOOL archiveContainsScriptSnapshot; - (instancetype)initWithDartMain:(NSURL*)dartMain packages:(NSURL*)packages - flxArchive:(NSURL*)flxArchive NS_DESIGNATED_INITIALIZER; + flutterAssets:(NSURL*)flutterAssets NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)flxArchive NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssets + NS_DESIGNATED_INITIALIZER; - (void)validate:(ValidationResult)result; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm index f7fabea4597fb..1f99c697cac86 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm @@ -8,26 +8,26 @@ @implementation FlutterDartSource @synthesize dartMain = _dartMain; @synthesize packages = _packages; -@synthesize flxArchive = _flxArchive; +@synthesize flutterAssets = _flutterAssets; @synthesize archiveContainsScriptSnapshot = _archiveContainsScriptSnapshot; #pragma mark - Convenience Initializers - (instancetype)init { - return [self initWithDartMain:nil packages:nil flxArchive:nil]; + return [self initWithDartMain:nil packages:nil flutterAssets:nil]; } #pragma mark - Designated Initializers - (instancetype)initWithDartMain:(NSURL*)dartMain packages:(NSURL*)packages - flxArchive:(NSURL*)flxArchive { + flutterAssets:(NSURL*)flutterAssets { self = [super init]; if (self) { _dartMain = [dartMain copy]; _packages = [packages copy]; - _flxArchive = [flxArchive copy]; + _flutterAssets = [flutterAssets copy]; NSFileManager* fileManager = [NSFileManager defaultManager]; @@ -44,11 +44,11 @@ - (instancetype)initWithDartMain:(NSURL*)dartMain return self; } -- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)flxArchive { +- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssets { self = [super init]; if (self) { - _flxArchive = [flxArchive copy]; + _flutterAssets = [flutterAssets copy]; _archiveContainsScriptSnapshot = YES; } @@ -79,7 +79,7 @@ - (void)validate:(ValidationResult)result { BOOL isValid = YES; - isValid &= CheckDartProjectURL(log, _flxArchive, @"FLX archive"); + isValid &= CheckDartProjectURL(log, _flutterAssets, @"Flutter assets"); if (!_archiveContainsScriptSnapshot) { isValid &= CheckDartProjectURL(log, _dartMain, @"Dart main"); @@ -92,7 +92,7 @@ - (void)validate:(ValidationResult)result { - (void)dealloc { [_dartMain release]; [_packages release]; - [_flxArchive release]; + [_flutterAssets release]; [super dealloc]; }