Skip to content

Commit

Permalink
Add JSNative utility class with static methods methods to efficiently…
Browse files Browse the repository at this point in the history
… manipulate typed JSInterop objects in cases where the member name is not known statically. These methods would be extension methods on JSObject if Dart supported extension methods. Update package js to export these methods. Implement in Dart2JS. Implement JS$ in dart2js.

BUG=
R=alanknight@google.com, sra@google.com

Review URL: https://codereview.chromium.org/2150313003 .
  • Loading branch information
jacob314 committed Jul 25, 2016
1 parent f5e157f commit 96ca5db
Show file tree
Hide file tree
Showing 23 changed files with 1,039 additions and 233 deletions.
14 changes: 13 additions & 1 deletion pkg/compiler/lib/src/js_backend/native_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class NativeData {
Map<MemberElement, NativeBehavior> nativeFieldStoreBehavior =
<FieldElement, NativeBehavior>{};

/// Prefix used to escape JS names that are not valid Dart names
/// when using JSInterop.
static const String _jsInteropEscapePrefix = r'JS$';

/// Returns `true` if [element] is explicitly marked as part of JsInterop.
bool _isJsInterop(Element element) {
return jsInteropNames.containsKey(element.declaration);
Expand Down Expand Up @@ -93,7 +97,7 @@ class NativeData {
if (jsInteropName != null && jsInteropName.isNotEmpty) {
return jsInteropName;
}
return element.isLibrary ? 'self' : element.name;
return element.isLibrary ? 'self' : getUnescapedJSInteropName(element.name);
}

/// Computes the name for [element] to use in the generated JavaScript. This
Expand Down Expand Up @@ -216,4 +220,12 @@ class NativeData {
FieldElement field, NativeBehavior behavior) {
nativeFieldStoreBehavior[field] = behavior;
}

/// Apply JS$ escaping scheme to convert possible escaped Dart names into
/// JS names.
String getUnescapedJSInteropName(String name) {
return name.startsWith(_jsInteropEscapePrefix)
? name.substring(_jsInteropEscapePrefix.length)
: name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ class ProgramBuilder {
for (Element e in elements) {
if (e is ClassElement && backend.isJsInterop(e)) {
e.declaration.forEachMember((_, Element member) {
var jsName =
backend.nativeData.getUnescapedJSInteropName(member.name);
if (!member.isInstanceMember) return;
if (member.isGetter || member.isField || member.isFunction) {
var selectors =
Expand All @@ -354,7 +356,7 @@ class ProgramBuilder {
var stubName = namer.invocationName(selector);
if (stubNames.add(stubName.key)) {
interceptorClass.callStubs.add(_buildStubMethod(stubName,
js.js('function(obj) { return obj.# }', [member.name]),
js.js('function(obj) { return obj.# }', [jsName]),
element: member));
}
}
Expand All @@ -367,10 +369,8 @@ class ProgramBuilder {
if (selectors != null && !selectors.isEmpty) {
var stubName = namer.setterForElement(member);
if (stubNames.add(stubName.key)) {
interceptorClass.callStubs.add(_buildStubMethod(
stubName,
js.js('function(obj, v) { return obj.# = v }',
[member.name]),
interceptorClass.callStubs.add(_buildStubMethod(stubName,
js.js('function(obj, v) { return obj.# = v }', [jsName]),
element: member));
}
}
Expand Down Expand Up @@ -447,7 +447,7 @@ class ProgramBuilder {
interceptorClass.callStubs.add(_buildStubMethod(
stubName,
js.js('function(receiver, #) { return receiver.#(#) }',
[parameters, member.name, parameters]),
[parameters, jsName, parameters]),
element: member));
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/compiler/lib/src/native/native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const Iterable<String> _allowedDartSchemePaths = const <String>[
'html_common',
'indexed_db',
'js',
'js_util',
'svg',
'_native_typed_data',
'web_audio',
Expand Down
9 changes: 5 additions & 4 deletions pkg/compiler/lib/src/ssa/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5619,14 +5619,15 @@ class SsaBuilder extends ast.Visitor
var filteredArguments = <HInstruction>[];
var parameterNameMap = new Map<String, js.Expression>();
params.orderedForEachParameter((ParameterElement parameter) {
// TODO(jacobr): throw if parameter names do not match names of property
// names in the class.
// TODO(jacobr): consider throwing if parameter names do not match
// names of properties in the class.
assert(parameter.isNamed);
HInstruction argument = arguments[i];
if (argument != null) {
filteredArguments.add(argument);
parameterNameMap[parameter.name] =
new js.InterpolatedExpression(positions++);
var jsName =
backend.nativeData.getUnescapedJSInteropName(parameter.name);
parameterNameMap[jsName] = new js.InterpolatedExpression(positions++);
}
i++;
});
Expand Down
6 changes: 6 additions & 0 deletions pkg/js/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.6.1
* Add js_util library of utility methods to efficiently manipulate typed
JavaScript interop objects in cases where the member name is not known
statically. These methods would be extension methods on JSObject if
Dart supported extension methods.

## 0.6.0

* Version 0.6.0 is a complete rewrite of `package:js`.
8 changes: 8 additions & 0 deletions pkg/js/lib/js_util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// Allows interoperability with Javascript APIs.
library js_util;

export 'dart:js_util';
2 changes: 1 addition & 1 deletion pkg/js/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: js
version: 0.6.0
version: 0.6.1
authors:
- Dart Team <misc@dartlang.org>
description: Access JavaScript from Dart.
Expand Down
35 changes: 35 additions & 0 deletions runtime/bin/bin.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'html_cc_file': '<(gen_source_dir)/html_gen.cc',
'html_common_cc_file': '<(gen_source_dir)/html_common_gen.cc',
'js_cc_file': '<(gen_source_dir)/js_gen.cc',
'js_util_cc_file': '<(gen_source_dir)/js_util_gen.cc',
'blink_cc_file': '<(gen_source_dir)/blink_gen.cc',
'indexeddb_cc_file': '<(gen_source_dir)/indexeddb_gen.cc',
'cached_patches_cc_file': '<(gen_source_dir)/cached_patches_gen.cc',
Expand Down Expand Up @@ -233,6 +234,38 @@
},
]
},
{
'target_name': 'generate_js_util_cc_file',
'type': 'none',
'toolsets':['host'],
'sources': [
'../../sdk/lib/js_util/dartium/js_util_dartium.dart',
],
'actions': [
{
'action_name': 'generate_js_util_cc',
'inputs': [
'../tools/gen_library_src_paths.py',
'<(builtin_in_cc_file)',
'<@(_sources)',
],
'outputs': [
'<(js_util_cc_file)',
],
'action': [
'python',
'tools/gen_library_src_paths.py',
'--output', '<(js_util_cc_file)',
'--input_cc', '<(builtin_in_cc_file)',
'--include', 'bin/builtin.h',
'--var_name', 'dart::bin::Builtin::js_util_source_paths_',
'--library_name', 'dart:js_util',
'<@(_sources)',
],
'message': 'Generating ''<(js_util_cc_file)'' file.'
},
]
},
{
'target_name': 'generate_blink_cc_file',
'type': 'none',
Expand Down Expand Up @@ -500,6 +533,7 @@
'generate_html_cc_file#host',
'generate_html_common_cc_file#host',
'generate_js_cc_file#host',
'generate_js_util_cc_file#host',
'generate_blink_cc_file#host',
'generate_indexeddb_cc_file#host',
'generate_cached_patches_cc_file#host',
Expand Down Expand Up @@ -1262,6 +1296,7 @@
'<(html_cc_file)',
'<(html_common_cc_file)',
'<(js_cc_file)',
'<(js_util_cc_file)',
'<(blink_cc_file)',
'<(indexeddb_cc_file)',
'<(cached_patches_cc_file)',
Expand Down
1 change: 1 addition & 0 deletions runtime/bin/builtin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Builtin::builtin_lib_props Builtin::builtin_libraries_[] = {
{ "dart:html", html_source_paths_, NULL, NULL, true },
{ "dart:html_common", html_common_source_paths_, NULL, NULL, true},
{ "dart:js", js_source_paths_, NULL, NULL, true},
{ "dart:js_util", js_util_source_paths_, NULL, NULL, true},
{ "dart:_blink", blink_source_paths_, NULL, NULL, true },
{ "dart:indexed_db", indexeddb_source_paths_, NULL, NULL, true },
{ "cached_patches.dart", cached_patches_source_paths_, NULL, NULL, true },
Expand Down
1 change: 1 addition & 0 deletions runtime/bin/builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Builtin {
static const char* html_source_paths_[];
static const char* html_common_source_paths_[];
static const char* js_source_paths_[];
static const char* js_util_source_paths_[];
static const char* blink_source_paths_[];
static const char* indexeddb_source_paths_[];
static const char* cached_patches_source_paths_[];
Expand Down
6 changes: 6 additions & 0 deletions sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ const Map<String, LibraryInfo> libraries = const {
maturity: Maturity.STABLE,
dart2jsPath: "js/dart2js/js_dart2js.dart"),

"js_util": const LibraryInfo(
"js_util/dartium/js_util_dartium.dart",
categories: "Client",
maturity: Maturity.STABLE,
dart2jsPath: "js_util/dart2js/js_util_dart2js.dart"),

"math": const LibraryInfo(
"math/math.dart",
categories: "Client,Server,Embedded",
Expand Down
1 change: 1 addition & 0 deletions sdk/lib/dart2dart.platform
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ indexed_db: indexed_db/dart2js/indexed_db_dart2js.dart
io: io/io.dart
isolate: isolate/isolate.dart
js: js/dart2js/js_dart2js.dart
js_util: js_util/dart2js/js_util_dart2js.dart
math: math/math.dart
mirrors: mirrors/mirrors.dart
nativewrappers: html/dart2js/nativewrappers.dart
Expand Down
1 change: 1 addition & 0 deletions sdk/lib/dart_client.platform
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ indexed_db: indexed_db/dart2js/indexed_db_dart2js.dart
io: unsupported:
isolate: isolate/isolate.dart
js: js/dart2js/js_dart2js.dart
js_util: js_util/dart2js/js_util_dart2js.dart
math: math/math.dart
mirrors: mirrors/mirrors.dart
nativewrappers: html/dart2js/nativewrappers.dart
Expand Down
1 change: 1 addition & 0 deletions sdk/lib/dart_shared.platform
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ indexed_db: indexed_db/dart2js/indexed_db_dart2js.dart
io: io/io.dart
isolate: isolate/isolate.dart
js: js/dart2js/js_dart2js.dart
js_util: js_util/dart2js/js_util_dart2js.dart
math: math/math.dart
mirrors: mirrors/mirrors.dart
nativewrappers: html/dart2js/nativewrappers.dart
Expand Down
2 changes: 1 addition & 1 deletion sdk/lib/js/dart2js/js_dart2js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ _callDartFunctionFastCaptureThis(callback, self, List arguments) {
return Function.apply(callback, [self]..addAll(arguments));
}

Function /*=F*/ allowInterop/*<F extends Function>*/(Function /*=F*/ f) {
Function/*=F*/ allowInterop/*<F extends Function>*/(Function/*=F*/ f) {
if (JS('bool', 'typeof(#) == "function"', f)) {
// Already supports interop, just use the existing function.
return f;
Expand Down

0 comments on commit 96ca5db

Please sign in to comment.