-
Notifications
You must be signed in to change notification settings - Fork 6k
Background Execution Implementation for iOS #5539
Changes from 5 commits
4aa0ace
59f4c05
2f3e0d8
ddbd5f3
b15e35c
9806222
3341e83
8373f88
f4d23c2
87d3d80
14e7aca
af25c66
2ff7690
48261fd
abcabee
2ec1ef4
8cc4394
81492d6
514df89
c6a955d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,23 +48,27 @@ namespace blink { | |
| #define BUILTIN_NATIVE_LIST(V) \ | ||
| V(Logger_PrintString, 1) \ | ||
| V(SaveCompilationTrace, 0) \ | ||
| V(ScheduleMicrotask, 1) | ||
| V(ScheduleMicrotask, 1) \ | ||
| V(LookupClosure, 3) \ | ||
| V(GetFunctionClassName, 1) \ | ||
| V(GetFunctionLibraryUrl, 1) \ | ||
| V(GetFunctionName, 1) | ||
|
|
||
| BUILTIN_NATIVE_LIST(DECLARE_FUNCTION); | ||
|
|
||
| void DartRuntimeHooks::RegisterNatives(tonic::DartLibraryNatives* natives) { | ||
| natives->Register({BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)}); | ||
| } | ||
|
|
||
| static Dart_Handle GetClosure(Dart_Handle builtin_library, const char* name) { | ||
| static Dart_Handle GetFunction(Dart_Handle builtin_library, const char* name) { | ||
| Dart_Handle getter_name = ToDart(name); | ||
| Dart_Handle closure = Dart_Invoke(builtin_library, getter_name, 0, nullptr); | ||
| DART_CHECK_VALID(closure); | ||
| return closure; | ||
| } | ||
|
|
||
| static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) { | ||
| Dart_Handle print = GetClosure(builtin_library, "_getPrintClosure"); | ||
| Dart_Handle print = GetFunction(builtin_library, "_getPrintClosure"); | ||
|
|
||
| Dart_Handle internal_library = Dart_LookupLibrary(ToDart("dart:_internal")); | ||
|
|
||
|
|
@@ -101,7 +105,7 @@ static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) { | |
| Dart_Handle schedule_microtask; | ||
| if (is_ui_isolate) { | ||
| schedule_microtask = | ||
| GetClosure(builtin_library, "_getScheduleMicrotaskClosure"); | ||
| GetFunction(builtin_library, "_getScheduleMicrotaskClosure"); | ||
| } else { | ||
| Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); | ||
| Dart_Handle method_name = | ||
|
|
@@ -125,7 +129,8 @@ static void InitDartIO(Dart_Handle builtin_library, | |
| DART_CHECK_VALID(Dart_SetField(platform_type, ToDart("_nativeScript"), | ||
| ToDart(script_uri))); | ||
| } | ||
| Dart_Handle locale_closure = GetClosure(builtin_library, "_getLocaleClosure"); | ||
| Dart_Handle locale_closure = | ||
| GetFunction(builtin_library, "_getLocaleClosure"); | ||
| DART_CHECK_VALID( | ||
| Dart_SetField(platform_type, ToDart("_localeClosure"), locale_closure)); | ||
| } | ||
|
|
@@ -223,4 +228,118 @@ void ScheduleMicrotask(Dart_NativeArguments args) { | |
| UIDartState::Current()->ScheduleMicrotask(closure); | ||
| } | ||
|
|
||
| void LookupClosure(Dart_NativeArguments args) { | ||
| Dart_Handle closure_name = Dart_GetNativeArgument(args, 0); | ||
| Dart_Handle library_name = Dart_GetNativeArgument(args, 1); | ||
| Dart_Handle class_name = Dart_GetNativeArgument(args, 2); | ||
| DART_CHECK_VALID(closure_name); | ||
| DART_CHECK_VALID(library_name); | ||
| DART_CHECK_VALID(class_name); | ||
|
|
||
| Dart_Handle library; | ||
| if (library_name == Dart_Null()) { | ||
| library = Dart_RootLibrary(); | ||
| } else { | ||
| library = Dart_LookupLibrary(library_name); | ||
| } | ||
| DART_CHECK_VALID(library); | ||
|
|
||
| Dart_Handle closure; | ||
| if (Dart_IsNull(class_name)) { | ||
| closure = Dart_GetClosure(library, closure_name); | ||
| } else { | ||
| Dart_Handle cls = Dart_GetClass(library, class_name); | ||
| DART_CHECK_VALID(cls); | ||
| if (Dart_IsNull(cls)) { | ||
| closure = Dart_Null(); | ||
| } else { | ||
| closure = Dart_GetStaticMethodClosure(library, cls, closure_name); | ||
| } | ||
| } | ||
| DART_CHECK_VALID(closure); | ||
|
|
||
| Dart_SetReturnValue(args, closure); | ||
| } | ||
|
|
||
| void GetFunctionLibraryUrl(Dart_NativeArguments args) { | ||
| Dart_Handle closure = Dart_GetNativeArgument(args, 0); | ||
| if (Dart_IsClosure(closure)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the incoming arg closure is not a closure, don't you need a check that it is a Function Object? further down you are calling Dart_FunctionOwner on it and this method will return an error if it is not a function object.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, good catch. I'll add a check. |
||
| closure = Dart_ClosureFunction(closure); | ||
| DART_CHECK_VALID(closure); | ||
| } | ||
|
|
||
| if (!Dart_IsFunction(closure)) { | ||
| Dart_SetReturnValue(args, Dart_Null()); | ||
| return; | ||
| } | ||
|
|
||
| Dart_Handle url = Dart_Null(); | ||
| Dart_Handle owner = Dart_FunctionOwner(closure); | ||
| if (Dart_IsInstance(owner)) { | ||
| owner = Dart_ClassLibrary(owner); | ||
| } | ||
| if (Dart_IsLibrary(owner)) { | ||
| url = Dart_LibraryUrl(owner); | ||
| DART_CHECK_VALID(url); | ||
| } | ||
| Dart_SetReturnValue(args, url); | ||
| } | ||
|
|
||
| void GetFunctionClassName(Dart_NativeArguments args) { | ||
| Dart_Handle closure = Dart_GetNativeArgument(args, 0); | ||
| Dart_Handle result; | ||
|
|
||
| if (Dart_IsClosure(closure)) { | ||
| closure = Dart_ClosureFunction(closure); | ||
| DART_CHECK_VALID(closure); | ||
| } | ||
|
|
||
| if (!Dart_IsFunction(closure)) { | ||
| Dart_SetReturnValue(args, Dart_Null()); | ||
| return; | ||
| } | ||
|
|
||
| bool is_static = false; | ||
| result = Dart_FunctionIsStatic(closure, &is_static); | ||
| DART_CHECK_VALID(result); | ||
| if (!is_static) { | ||
| Dart_SetReturnValue(args, Dart_Null()); | ||
| return; | ||
| } | ||
|
|
||
| result = Dart_FunctionOwner(closure); | ||
| DART_CHECK_VALID(result); | ||
|
|
||
| if (Dart_IsLibrary(result) || !Dart_IsInstance(result)) { | ||
| Dart_SetReturnValue(args, Dart_Null()); | ||
| return; | ||
| } | ||
|
|
||
| result = Dart_ClassName(result); | ||
| DART_CHECK_VALID(result); | ||
| Dart_SetReturnValue(args, result); | ||
| } | ||
|
|
||
| void GetFunctionName(Dart_NativeArguments args) { | ||
| Dart_Handle func = Dart_GetNativeArgument(args, 0); | ||
| DART_CHECK_VALID(func); | ||
|
|
||
| if (Dart_IsClosure(func)) { | ||
| func = Dart_ClosureFunction(func); | ||
| DART_CHECK_VALID(func); | ||
| } | ||
|
|
||
| if (!Dart_IsFunction(func)) { | ||
| Dart_SetReturnValue(args, Dart_Null()); | ||
| return; | ||
| } | ||
|
|
||
| Dart_Handle result = Dart_FunctionName(func); | ||
| if (Dart_IsError(result)) { | ||
| Dart_PropagateError(result); | ||
| return; | ||
| } | ||
| Dart_SetReturnValue(args, result); | ||
| } | ||
|
|
||
| } // namespace blink | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // Copyright 2018 The Chromium Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| part of dart.ui; | ||
|
|
||
| /// Functionality for Flutter plugin authors. | ||
| abstract class PluginUtilities { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to write tests of these methods? If so, I think we should have some.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it should be. I'll go ahead and write some.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests added in latest commit.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you talk about why you need these?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are needed to lookup callback methods which are top-level functions or static methods in a class by name. The general flow is something like this:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am concerned about this API. From most serious to least serious concerns:
Can you talk more broadly about what problem these methods solve? Maybe we can find a safer solution to this problem.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I feel like a lot of your concerns could be addressed by an opaque class with static methods that could:
This way, we can avoid having these strings visible and modifiable by the user, prevent abuse of what are basically Dart VM APIs, and make caching difficult. The only difficulty I see with this approach is passing said object over method channels since it would have to be serialized and deserialized in such a way that the data itself still remains private, but anyone can listen to a method channel if they know the name of the channel. I'd have to give this part some thought...
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your latest update resolves my concerns. Thanks! |
||
| /// Get a closure instance for a specific static or top-level function given | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should warn that it will fail to find the closure and return null if the closure is not otherwise used in the program. That is, it may get tree-shaken away in an AOT build.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| /// a name. | ||
| /// | ||
| /// **Warning:** if the function corresponding with `name` is not referenced | ||
| /// explicitly (e.g., invoked, passed as a closure, etc.) it may be optimized | ||
| /// out of the compiled application. To prevent this, avoid hard coding names | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: hardcoding
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| /// as [String]s and prefer to use `PluginUtilities.getNameOfFunction` to | ||
| /// retrieve the function's name. | ||
| /// | ||
| /// `name` is the name of the function we want a closure for. This is a | ||
| /// required parameter. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typical wording is 'This must not be null.'
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| /// | ||
| /// `libraryPath` is the path which points to the library which contains the | ||
| /// function of interest. This path can be retrieved using | ||
| /// [PluginUtilities.getPathForFunctionLibrary]. If not specified, the root | ||
| /// library will be used for the function lookup. | ||
| /// | ||
| /// `className` is the name of the class in which function `name` is defined. | ||
| /// This parameter is required for retrieving closures for static methods. If | ||
| /// not provided, `name` is assumed to be referring to a top-level function. | ||
| /// | ||
| /// Returns a closure for `name` as a [Function] if the lookup was successful. | ||
| /// Otherwise, `null` is returned on error. | ||
| static Function getClosureByName({String name, String libraryPath, String className}) { | ||
| if (name == null) { | ||
| throw new ArgumentError.notNull('name'); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and elsewhere: for null checks, generally prefer: We typically reserve ArgumentError for malformed values -- e.g. we're passed a list of length 3 when we document it must be 4, etc.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| return _lookupClosure(name, libraryPath, className); | ||
| } | ||
|
|
||
| /// Get the path for the library which contains a given method. | ||
| /// | ||
| /// `closure` is the closure of the function that we want to find the library | ||
| /// path for. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add: 'This must not be null.'
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| /// | ||
| /// Returns a [String] representing the path to the library which contains | ||
| /// `closure`. Returns `null` if an error occurs or `closure` is not found | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trailing period.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| static String getPathForFunctionLibrary(Function closure) { | ||
| if (closure == null) { | ||
| throw new ArgumentError.notNull('closure'); | ||
| } | ||
| return _getFunctionLibraryUrl(closure); | ||
| } | ||
|
|
||
| /// Get the name of a [Function] as a [String]. | ||
| /// | ||
| /// Returns a [String] representing the name of the function if the function | ||
| /// exists, otherwise `null` is returned. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document that closure must not be null.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| static String getNameOfFunction(Function closure) { | ||
| if (closure == null) { | ||
| throw new ArgumentError.notNull('closure'); | ||
| } | ||
| return _getFunctionName(closure); | ||
| } | ||
|
|
||
| /// Get the name of a class containing [Function] as a [String]. | ||
| /// | ||
| /// Returns a [String] representing the name of the function if the function | ||
| /// exists and is a member of a class, otherwise `null` is returned. | ||
| static String getNameOfFunctionClass(Function closure) { | ||
| if(closure == null) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| throw new ArgumentError.notNull('closure'); | ||
| } | ||
| return _getNameOfFunctionClass(closure); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code will probably propagate error handles correctly, but I like to check Dart_Handle return values with DART_CHECK_VALID() unless there's some reason not to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.