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

Unified Diff: frog/leg/lib/native_helper.dart

Issue 9750003: Write our JS blobs for handling native classes in Dart. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 9 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
Index: frog/leg/lib/native_helper.dart
===================================================================
--- frog/leg/lib/native_helper.dart (revision 0)
+++ frog/leg/lib/native_helper.dart (revision 0)
@@ -0,0 +1,277 @@
+// Copyright (c) 2012, 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.
+
+String typeNameInChrome(obj) {
+ String name = JS('String', "#.constructor.name", obj);
+ if (name == 'Window') return 'DOMWindow';
+ return name;
+}
+
+String typeNameInFirefox(obj) {
+ String name = constructorNameFallback(obj);
+ if (name == 'Window') return 'DOMWindow';
+ if (name == 'Document') return 'HTMLDocument';
+ if (name == 'XMLDocument') return 'Document';
+ return name;
+}
+
+String typeNameInIE(obj) {
+ String name = constructorNameFallback(obj);
+ if (name == 'Window') return 'DOMWindow';
+ // IE calls both HTML and XML documents 'Document', so we check for the
+ // xmlVersion property, which is the empty string on HTML documents.
+ if (name == 'Document' && JS('bool', '!!#.xmlVersion', obj)) return 'Document';
floitsch 2012/03/28 23:36:33 80 chars.
ngeoffray 2012/03/29 11:38:40 Done.
+ if (name == 'Document') return 'HTMLDocument';
+ return name;
+}
+
+String constructorNameFallback(obj) {
+ var constructor = JS('var', "#.constructor", obj);
+ if (JS('String', "typeof(#)", constructor) === 'function') {
+ // The constructor isn't null or undefined at this point. Try
+ // to grab hold of its name.
+ var name = JS('var', '#.name', constructor);
+ // If the name is a non-empty string, we use that as the type
+ // name of this object. On Firefox, we often get 'Object' as
+ // the constructor name even for more specialized objects so
+ // we have to fall through to the toString() based implementation
+ // below in that case.
+ if (JS('String', "typeof(#)", name) === 'string'
+ && !name.isEmpty()
+ && name !== 'Object') {
+ return name;
+ }
+ }
+ String string = JS('String', 'Object.prototype.toString.call(#)', obj);
+ return string.substring(8, string.length - 1);
+}
+
+
+/**
+ * Returns the function to use to get the type name of an object.
+ */
+Function getTypeNameOfFunction() {
floitsch 2012/03/28 23:36:33 maybe getFunctionForTypeNameOf ? Otherwise it read
ngeoffray 2012/03/29 11:38:40 Done.
+ // If we're not in the browser, we're almost certainly running on v8.
+ if (JS('String', 'typeof(navigator)') !== 'object') return typeNameInChrome;
+
+ String userAgent = JS('String', "navigator.userAgent");
+ if (userAgent.contains(const RegExp('Chrome|DumpRenderTree'))) {
+ return typeNameInChrome;
+ } else if (userAgent.contains('Firefox')) {
+ return typeNameInFirefox;
+ } else if (userAgent.contains('MSIE')) {
+ return typeNameInIE;
+ } else {
+ return constructorNameFallback;
+ }
+}
+
+
+/**
+ * Cached value for the function to use to get the type name of an
+ * object.
+ */
+Function _getTypeNameOf;
sra1 2012/03/27 18:05:27 Could you initialize this with the following funct
ngeoffray 2012/03/29 10:20:48 I cannot right now, as it's not valid Dart code to
+
+/**
+ * Returns the type name of [obj].
+ */
+String getTypeNameOf(var obj) {
+ if (_getTypeNameOf === null) _getTypeNameOf = getTypeNameOfFunction();
+ return _getTypeNameOf(obj);
+}
+
+/**
+ * Sets a JavaScript property on an object.
+ */
+void defineProperty(var obj, String property, var value) {
+ JS('void', """Object.defineProperty(#, #,
+ {value: #, enumerable: false, writable: false, configurable: true});""",
+ obj,
+ property,
+ value);
+}
+
+/**
+ * Helper method to throw a [NoSuchMethodException] for a invalid call
+ * on a native object.
+ */
+void throwNoSuchMethod(obj, name, arguments) {
+ throw new NoSuchMethodException(obj, name, arguments);
+}
+
+/**
+ * This method looks up the type name of [obj] in [methods]. If it
+ * cannot find it, it looks into the [_dynamicMetadata] array. If the
+ * method can still not be found, it creates a method that will throw
+ * a [NoSuchMethodException].
+ *
+ * Once it has a method, the prototype of [obj] is patched with that
+ * method, on the property [name]. The method is then invoked.
+ *
+ * This method returns the result of invoking the found method.
+ */
+dynamicBind(var obj,
+ String name,
+ var methods,
floitsch 2012/03/28 23:36:33 Add comment what the type of methods is (a native
ngeoffray 2012/03/29 11:38:40 Done.
+ List arguments) {
+ String tag = getTypeNameOf(obj);
+ var method = JS('var', '#[#]', methods, tag);
+
+ if (method === null && _dynamicMetadata !== null) {
floitsch 2012/03/28 23:36:33 how can _dynamicMetadata be null?
sra1 2012/03/29 01:14:23 It is not generated for programs that call only me
+ for (int i = 0; i < _dynamicMetadata.length; i++) {
+ MetaInfo entry = _dynamicMetadata[i];
+ if (entry.set.contains(tag)) {
+ method = JS('var', '#[#]', methods, entry.tag);
+ if (method !== null) break;
+ }
+ }
+ }
+
+ if (method === null) {
+ method = JS('var', "#['Object']", methods);
+ }
+
+ if (method === null) {
+ method = JS('var',
+ 'function () {'
+ '#(#, #, Array.prototype.slice.call(arguments));'
sra1 2012/03/29 01:14:23 There is a missing exact type check here. class A
ngeoffray 2012/03/29 11:38:40 Done.
+ '}',
+ DART_CLOSURE_TO_JS(throwNoSuchMethod), obj, name);
+ }
+
+ var nullCheckMethod = JS('var',
+ 'function() {'
+ 'var res = #.apply(this, Array.prototype.slice.call(arguments));'
+ 'return res === null ? (void 0) : res;'
+ '}',
+ method);
+
+ var proto = JS('var', 'Object.getPrototypeOf(#)', obj);
+ if (JS('bool', '!#.hasOwnProperty(#)', proto, name)) {
floitsch 2012/03/28 23:36:33 how can this happen?
sra1 2012/03/29 01:14:23 This prevents (new Object()).dynamic.foo() from
+ defineProperty(proto, name, nullCheckMethod);
+ }
+
+ return JS('var', '#.apply(#, #)', nullCheckMethod, obj, arguments);
+}
+
+/**
+ * Code for doing the dynamic dispatch on JavaScript prototypes that are not
+ * available at compile-time. Each property of a native Dart class
+ * is registered through this function, which is called with the
+ * following pattern:
+ *
+ * dynamicFunction('propertyName').prototypeName = // JS code
+ *
+ * What this function does is:
+ * - Creates a map of { prototypeName: JS code }.
+ * - Attaches 'propertyName' to the JS Object prototype that will
+ * intercept at runtime all calls to propertyName.
+ * - Sets the value of 'propertyName' to the returned method from
+ * [dynamicBind].
+ *
+ */
+dynamicFunction(name) {
+ var f = JS('var', 'Object.prototype[#]', name);
+ if (f !== null && JS('bool', '!!#.methods', f)) {
+ return JS('var', '#.methods', f);
+ }
+
+ // TODO(ngeoffray): We could make this a map if the code we
+ // generate plays well with a Dart map.
+ var methods = JS('var', '{}');
+ // If there is a method attached to the Dart Object class, use it as
+ // the method to call in case no method is registered for that type.
+ var dartMethod = JS('var', 'Object.getPrototypeOf(#)[#]', new Object(), name);
floitsch 2012/03/28 23:36:33 const Object() would work too. No preference.
ngeoffray 2012/03/29 11:38:40 Good point. Done.
+ if (dartMethod !== null) JS('void', "#['Object'] = #", methods, dartMethod);
+
+ var bind = JS('var',
+ 'function() {'
+ 'return #(this, #, #, Array.prototype.slice.call(arguments));'
+ '}',
+ DART_CLOSURE_TO_JS(dynamicBind), name, methods);
+
+ JS('void', '#.methods = #', bind, methods);
+ defineProperty(JS('var', 'Object.prototype'), name, bind);
+ return methods;
+}
+
+/**
+ * This class encodes the class hierarchy when we need it for dynamic
+ * dispatch.
+ */
+class MetaInfo {
+ /**
+ * The type name this [MetaInfo] relates to.
+ */
+ String tag;
+
+ /**
+ * A string containing the names of subtypes of [tag], separated by
+ * '|'.
+ */
+ String tags;
+
+ /**
+ * A list of names of subtypes of [tag].
+ */
+ Set<String> set;
+
+ MetaInfo(this.tag, this.tags, this.set);
+}
+
+List<MetaInfo> get _dynamicMetadata() {
+ if (JS('var', 'typeof(\$dynamicMetadata)') === 'undefined') {
floitsch 2012/03/28 23:36:33 why is \$dynamicMetadata not a normal Dart global
ngeoffray 2012/03/29 11:38:40 Because it is shared with isolates. Making it glob
+ _dynamicMetadata = <MetaInfo>[];
+ }
+ return JS('var', '\$dynamicMetadata');
+}
+
+void set _dynamicMetadata(List<String> table) {
+ JS('void', '\$dynamicMetadata = #', table);
+}
+
+/**
+ * Builds the metadata used for encoding the class hierarchy of native
+ * classes. The following example:
+ *
+ * class A native "*A" {}
+ * class B native "*B" {}
floitsch 2012/03/28 23:36:33 extends A
ngeoffray 2012/03/29 11:38:40 Done.
+ *
+ * Will generate:
floitsch 2012/03/28 23:36:33 Will generate a list of: (the inputTable)
+ * ['A', 'A|B']
floitsch 2012/03/28 23:36:33 explain. ex: where 'A' is the class the informatio
ngeoffray 2012/03/29 11:38:40 I'm reluctant on adding comments as this is copied
+ *
+ * This method turns the array into a list of [MetaInfo] objects.
+ */
+void dynamicSetMetadata(List<List<String>> inputTable) {
+ _dynamicMetadata = <MetaInfo>[];
+ for (int i = 0; i < inputTable.length; i++) {
+ String tag = inputTable[i][0];
+ String tags = inputTable[i][1];
+ Set<String> set = new Set<String>();
+ List<String> tagNames = tags.split('|');
+ for (int j = 0; j < tagNames.length; j++) {
+ set.add(tagNames[j]);
+ }
+ _dynamicMetadata.add(new MetaInfo(tag, tags, set));
+ }
+}
+
+// Initialized by the compiler.
floitsch 2012/03/28 23:36:33 Describe type. (if I see correctly a native JS has
ngeoffray 2012/03/29 11:38:40 This code was removed.
+var isChecksHelper;
+
+// This method will be called for 'is' checks on native types.
+// It takes the object on which the 'is' check is being done, and the
+// property name for the type check. The method patches the real
+// prototype of the object with the value from the Dart object
+// (see [generateNativeClass]).
+bool dynamicIsCheck(obj, String typeName) {
+ if (isJsArray(obj)) return false;
+ // Check if the Dart object corresponding to this class has the property.
+ var res = JS('bool', '!!#[#][#]', isChecksHelper, getTypeNameOf(obj),
+ typeName);
+ var proto = JS('var', 'Object.getPrototypeOf(#)', obj);
+ defineProperty(proto, typeName, res);
+ return res;
+}

Powered by Google App Engine
This is Rietveld 408576698