Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(204)

Unified Diff: sdk/lib/js/dartium/js_dartium.dart

Issue 1391353002: Dartium typed js interop work dart repo side of the change. Also tries to get (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/html/dartium/html_dartium.dart ('k') | tests/html/js_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/js/dartium/js_dartium.dart
diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart
index 186bde7d613cac0f9b3f7b2d60e5ace528533329..eaf78716687ca532518dfecc137a45c1612508ed 100644
--- a/sdk/lib/js/dartium/js_dartium.dart
+++ b/sdk/lib/js/dartium/js_dartium.dart
@@ -92,6 +92,8 @@ import 'dart:nativewrappers';
import 'dart:math' as math;
import 'dart:mirrors' as mirrors;
import 'dart:html' as html;
+import 'dart:indexed_db' as indexed_db;
+import 'dart:typed_data';
// Pretend we are always in checked mode as we aren't interested in users
// running Dartium code outside of checked mode.
@@ -204,10 +206,12 @@ class _DeclarationSet {
* Temporary method that we hope to remove at some point. This method should
* generally only be called by machine generated code.
*/
-void registerJsInterfaces(List<Type> classes) {
- if (_finalized == true) {
- throw 'JSInterop class registration already finalized';
- }
+
+void registerJsInterfaces([List<Type> classes]) {
+ // This method is now obsolete in Dartium.
+}
+
+void _registerJsInterfaces(List<Type> classes) {
for (Type type in classes) {
if (!_jsInterfaceTypes.add(type)) continue; // Already registered.
mirrors.ClassMirror typeMirror = mirrors.reflectType(type);
@@ -252,19 +256,226 @@ void registerJsInterfaces(List<Type> classes) {
_finalizeJsInterfaces() native "Js_finalizeJsInterfaces";
+String _getJsName(mirrors.DeclarationMirror mirror) {
+ for (var annotation in mirror.metadata) {
+ if (mirrors.MirrorSystem.getName(annotation.type.simpleName) == "Js") {
+ mirrors.LibraryMirror library = annotation.type.owner;
+ var uri = library.uri;
+ // make sure the annotation is from package://js
+ if (uri.scheme == 'package' && uri.path == 'js/js.dart') {
+ try {
+ var name = annotation.reflectee.name;
+ return name != null ? name : "";
+ } catch (e) {
+ }
+ }
+ }
+ }
+ return null;
+}
+
+bool _hasJsName(mirrors.DeclarationMirror mirror) =>
+ _getJsName(mirror) != null;
+
+
+_getJsMemberName(mirrors.DeclarationMirror mirror) {
+ var name = _getJsName(mirror);
+ return name == null || name.isEmpty ? _getDeclarationName(mirror) :
+ name;
+}
+
+// TODO(jacobr): handle setters correctyl.
+String _getDeclarationName(mirrors.DeclarationMirror declaration) {
+ var name = mirrors.MirrorSystem.getName(declaration.simpleName);
+ if (declaration is mirrors.MethodMirror && declaration.isSetter) {
+ assert(name.endsWith("="));
+ name = name.substring(0, name.length - 1);
+ }
+ return name;
+}
+
+final _JS_LIBRARY_PREFIX = "js_library";
+final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST";
+
+String _accessJsPath(String path) =>
+ "${_JS_LIBRARY_PREFIX}.context${path.split(".").map((p) => "['$p']").join('')}";
+
+void addMemberHelper(mirrors.MethodMirror declaration, String path, StringBuffer sb, {bool isStatic: false, String memberName}) {
+ var jsName = _getJsMemberName(declaration);
+ path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName;
+ var name = memberName != null ? memberName : _getDeclarationName(declaration);
+ if (declaration.isConstructor) {
+ sb.write("factory");
+ } else if (isStatic) {
+ sb.write("static");
+ } else {
+ sb.write("patch");
+ }
+ sb.write(" ");
+ if (declaration.isGetter) {
+ sb.write("get $name => ${_accessJsPath(path)};");
+ } else if (declaration.isSetter) {
+ sb.write("set $name(v) => ${_accessJsPath(path)} = v;");
+ } else {
+ sb.write("$name(");
+ bool hasOptional = false;
+ int i = 0;
+ var args = <String>[];
+ for (var p in declaration.parameters) {
+ assert(!p.isNamed); // XXX throw
+ assert(!p.hasDefaultValue);
+ if (i > 0) {
+ sb.write(", ");
+ }
+ if (p.isOptional && !hasOptional) {
+ sb.write("[");
+ hasOptional = true;
+ }
+ var arg = "p$i";
+ args.add(arg);
+ sb.write(arg);
+ if (p.isOptional) {
+ sb.write("=${_UNDEFINED_VAR}");
+ }
+ i++;
+ }
+ if (hasOptional) {
+ sb.write("]");
+ }
+ // TODO(jacobr):
+ sb.write(") => ");
+ if (declaration.isConstructor) {
+ sb.write("new ${_JS_LIBRARY_PREFIX}.JsObject(");
+ }
+ sb
+ ..write(_accessJsPath(path))
+ ..write(declaration.isConstructor ? "," : ".apply(")
+ ..write("[${args.join(",")}]");
+
+ if (hasOptional) {
+ sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()");
+ }
+ sb.write(");");
+ }
+ sb.write("\n");
+}
+
+// TODO(jacobr): make this check more robust.
+bool _isExternal(mirrors.MethodMirror mirror) =>
+ mirror.source != null && mirror.source.startsWith("external ");
+
+List<String> _generateExternalMethods() {
+ var staticCodegen = <String>[];
+ mirrors.currentMirrorSystem().libraries.forEach((uri, library) {
+ var sb = new StringBuffer();
+ String jsLibraryName = _getJsName(library);
+ library.declarations.forEach((name, declaration) {
+ if (declaration is mirrors.MethodMirror) {
+ if (_isExternal(declaration) && (_hasJsName(declaration) || jsLibraryName != null)) {
+ addMemberHelper(declaration, jsLibraryName, sb);
+ }
+ } else if (declaration is mirrors.ClassMirror) {
+ mirrors.ClassMirror clazz = declaration;
+ if (_hasJsName(clazz)) {
+ // TODO(jacobr): verify class implements JavaScriptObject.
+ assert(clazz.hasReflectedType);
+ jsInterfaceTypes.add(clazz.reflectedType);
+ String jsClassName = _getJsMemberName(clazz);
+ var className = mirrors.MirrorSystem.getName(clazz.simpleName);
+ var sbPatch = new StringBuffer();
+ clazz.declarations.forEach((name, declaration) {
+ if (declaration is! mirrors.MethodMirror || !_isExternal(declaration)) return;
+ if (declaration.isFactoryConstructor) {
+ sbPatch.write(" factory ${className}({");
+ int i = 0;
+ var args = <String>[];
+ for (var p in declaration.parameters) {
+ assert(p.isNamed); // XXX throw
+ args.add(mirrors.MirrorSystem.getName(p.simpleName));
+ i++;
+ }
+ sbPatch
+ ..write(args.map((name) => '$name:${_UNDEFINED_VAR}').join(", "))
+ ..write("}) {\n"
+ " var ret = new ${_JS_LIBRARY_PREFIX}.JsObject.jsify({});\n");
+ i = 0;
+ for (var p in declaration.parameters) {
+ assert(p.isNamed); // XXX throw
+ var name = args[i];
+ var jsName = mirrors.MirrorSystem.getName(p.simpleName);
+ // XXX apply name conversion rules.
+ sbPatch.write(" if($name != ${_UNDEFINED_VAR}) ret['$jsName'] = $name;\n");
+ i++;
+ }
+
+ sbPatch.write(" return ret;\n"
+ " }\n");
+ } else if (declaration.isConstructor) {
+ sbPatch.write(" ");
+ addMemberHelper(declaration,
+ (jsLibraryName != null && jsLibraryName.isNotEmpty) ? "${jsLibraryName}" : "",
+ sbPatch,
+ isStatic: true,
+ memberName: className);
+ }
+ });
+
+ clazz.staticMembers.forEach((memberName, member) {
+ if (_isExternal(member)) {
+ sbPatch.write(" ");
+ addMemberHelper(member,
+ (jsLibraryName != null && jsLibraryName.isNotEmpty) ? "${jsLibraryName}.${jsClassName}" : jsClassName,
+ sbPatch,
+ isStatic: true);
+ }
+ });
+ if (sbPatch.isNotEmpty) {
+ sb.write("""
+patch class $className {
+$sbPatch
+}
+""");
+ }
+ }
+ }
+ });
+ if (sb.isNotEmpty) {
+ staticCodegen
+ ..add(uri.toString())
+ ..add("${uri}_js_interop_patch.dart")
+ ..add("""
+import 'dart:js' as ${_JS_LIBRARY_PREFIX};
+
+/**
+ * Placeholder object for cases where we need to determine exactly how many
+ * args were passed to a function.
+ */
+const ${_UNDEFINED_VAR} = const Object();
+
+${sb}
+""");
+ }
+ });
+
+ return staticCodegen;
+}
+
/**
* Generates a part file defining source code for JsObjectImpl and related
* classes. This calass is needed so that type checks for all registered JavaScript
* interop classes pass.
*/
-String _generateJsObjectImplPart() {
- Iterable<Type> types = jsInterfaceTypes;
+List<String> _generateInteropPatchFiles() {
+ var ret = _generateExternalMethods();
var libraryPrefixes = new Map<mirrors.LibraryMirror, String>();
var prefixNames = new Set<String>();
var sb = new StringBuffer();
var implements = <String>[];
- for (var type in types) {
+ var implementsArray = <String>[];
+ var listMirror = mirrors.reflectType(List);
+
+ for (var type in jsInterfaceTypes) {
mirrors.ClassMirror typeMirror = mirrors.reflectType(type);
mirrors.LibraryMirror libraryMirror = typeMirror.owner;
var prefixName;
@@ -284,16 +495,19 @@ String _generateJsObjectImplPart() {
prefixNames.add(prefixName);
libraryPrefixes[libraryMirror] = prefixName;
}
- implements.add(
+ var isArray = typeMirror.isSubtypeOf(listMirror);
+ (isArray ? implementsArray : implements).add(
'${prefixName}.${mirrors.MirrorSystem.getName(typeMirror.simpleName)}');
}
libraryPrefixes.forEach((libraryMirror, prefix) {
sb.writeln('import "${libraryMirror.uri}" as $prefix;');
});
- var implementsClause =
- implements.isEmpty ? "" : "implements ${implements.join(', ')}";
+ buildImplementsClause(classes) =>
+ classes.isEmpty ? "" : "implements ${classes.join(', ')}"
+ var implementsClause = buildImplementsClause(implements);
// TODO(jacobr): only certain classes need to be implemented by
- // Function and Array.
+ // JsFunctionImpl.
+ var allTypes = []..addAll(implements)..addAll(implementsArray);
sb.write('''
class JsObjectImpl extends JsObject $implementsClause {
JsObjectImpl.internal() : super.internal();
@@ -303,11 +517,17 @@ class JsFunctionImpl extends JsFunction $implementsClause {
JsFunctionImpl.internal() : super.internal();
}
-class JsArrayImpl<E> extends JsArray<E> $implementsClause {
+class JsArrayImpl<E> extends JsArray<E> ${buildImplementsClause(implementsArray)} {
JsArrayImpl.internal() : super.internal();
}
+
+_registerAllJsInterfaces() {
+ _registerJsInterfaces([${allTypes.join(", ")}]);
+}
+
''');
- return sb.toString();
+ ret..addAll(["dart:js", "JsInteropImpl.dart", sb.toString()]);
+ return ret;
}
// Start of block of helper methods facilitating emulating JavaScript Array
@@ -479,6 +699,18 @@ JsObject get context {
return _cachedContext;
}
+_maybeWrap(o) {
+ var wrapped = html.wrap_jso_no_SerializedScriptvalue(o);
+ if (identical(wrapped, o)) return o;
+ return (wrapped is html.Blob
+ || wrapped is html.Event
+ || wrapped is indexed_db.KeyRange
+ || wrapped is html.ImageData
+ || wrapped is html.Node
+ || wrapped is TypedData
+ || wrapped is html.Window) ? wrapped : o;
+}
+
/**
* Get the dart wrapper object for object. Top-level so we
* we can access it from other libraries without it being
@@ -501,17 +733,10 @@ void setDartHtmlWrapperFor(JsObject object, wrapper) {
* wrap_jso defined in dart:html.
*/
unwrap_jso(dartClass_instance) {
- try {
- if (dartClass_instance != null)
- return dartClass_instance is NativeFieldWrapperClass2 ?
- dartClass_instance.blink_jsObject : dartClass_instance;
- else
- return null;
- } catch(NoSuchMethodException) {
- // No blink_jsObject then return the dartClass_instance is probably an
- // array that was already converted to a Dart class e.g., Uint8ClampedList.
+ if (dartClass_instance is NativeFieldWrapperClass2 && dartClass_instance is! JsObject)
+ return dartClass_instance.blink_jsObject;
+ else
return dartClass_instance;
- }
}
/**
@@ -604,7 +829,7 @@ class JsObject extends NativeFieldWrapperClass2 {
*/
operator [](property) {
try {
- return _operator_getter(property);
+ return _maybeWrap(_operator_getter(property));
} catch (e) {
// Re-throw any errors (returned as a string) as a DomException.
throw new html.DomException.jsInterop(e);
@@ -685,11 +910,7 @@ class JsObject extends NativeFieldWrapperClass2 {
*/
callMethod(String method, [List args]) {
try {
- if (args != null) {
- for (var i = 0; i < args.length; i++)
- args[i] = unwrap_jso(args[i]);
- }
- return _callMethod(method, args);
+ return _maybeWrap(_callMethod(method, args));
} catch (e) {
if (hasProperty(method)) {
// Return a DomException if DOM call returned an error.
@@ -753,6 +974,29 @@ class JsObject extends NativeFieldWrapperClass2 {
_callMethod(String name, List args) native "JsObject_callMethod";
}
+// JavaScript interop methods that do not automatically wrap to dart:html types.
+// Warning: this API is not exposed to dart:js.
+class JsNative {
+ static getProperty(JsObject o, name) {
+ return o._operator_getter(name);
+ }
+
+ static callMethod(JsObject o, String method, List args) {
+ return o._callMethod(method, args);
+ }
+
+ static getArrayIndex(JsArray array, int index) {
+ array._checkIndex(index);
+ return getProperty(array, index);
+ }
+
+ /**
+ * Same behavior as new JsFunction.withThis except that JavaScript "this" is not
+ * wrapped.
+ */
+ static JsFunction withThis(Function f) native "JsFunction_withThisNoWrap";
+}
+
/**
* Proxies a JavaScript Function object.
*/
@@ -769,7 +1013,10 @@ class JsFunction extends JsObject implements Function {
* Invokes the JavaScript function with arguments [args]. If [thisArg] is
* supplied it is the value of `this` for the invocation.
*/
- dynamic apply(List args, {thisArg}) native "JsFunction_apply";
+ dynamic apply(List args, {thisArg}) =>
+ _maybeWrap(_apply(args, thisArg: thisArg));
+
+ dynamic _apply(List args, {thisArg}) native "JsFunction_apply";
noSuchMethod(Invocation invocation) {
if (invocation.isMethod && invocation.memberName == #call) {
@@ -911,3 +1158,26 @@ Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) =>
a5 = _UNDEFINED, a6 = _UNDEFINED, a7 = _UNDEFINED, a8 = _UNDEFINED,
a9 = _UNDEFINED, a10 = _UNDEFINED]) => jsFunction._applyDebuggerOnly(
_stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
+
+// This method is a no-op in Dartium.
+// TODO(jacobr): tag methods so we can throw if a Dart method is passed to
+// JavaScript using the new interop without calling allowInterop.
+Function allowInterop(Function f) => f;
+
+Expando<JsFunction> _interopCaptureThisExpando = new Expando<JsFunction>();
+
+Function allowInteropCaptureThis(Function f) {
+ if (f is JsFunction) {
+ // Behavior when the function is already a JS function is unspecified.
+ throw new ArgumentError(
+ "Function is already a JS function so cannot capture this.");
+ return f;
+ } else {
+ var ret = _interopCaptureThisExpando[f];
+ if (ret == null) {
+ ret = new JsFunction.withThis(f);
+ _interopCaptureThisExpando[f] = ret;
+ }
+ return ret;
+ }
+}
« no previous file with comments | « sdk/lib/html/dartium/html_dartium.dart ('k') | tests/html/js_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698