| 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,243 @@
|
| +// 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 chromeTypeNameOf(obj) {
|
| + String name = JS('String', "#.constructor.name", obj);
|
| + if (name == 'Window') return 'DOMWindow';
|
| + return name;
|
| +}
|
| +
|
| +String firefoxTypeNameOf(obj) {
|
| + String name = constructorNameWithFallback(obj);
|
| + if (name == 'Window') return 'DOMWindow';
|
| + if (name == 'Document') return 'HTMLDocument';
|
| + if (name == 'XMLDocument') return 'Document';
|
| + return name;
|
| +}
|
| +
|
| +String ieTypeNameOf(obj) {
|
| + var name = constructorNameWithFallback(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';
|
| + if (name == 'Document') return 'HTMLDocument';
|
| + return name;
|
| +}
|
| +
|
| +String constructorNameWithFallback(obj) {
|
| + var constructor = JS('var', "#.constructor", obj);
|
| + if (JS('bool', "typeof(#) == 'function'", constructor)) {
|
| + // 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('bool', "typeof(#) == 'string'", name)
|
| + && JS('bool', '#', name)
|
| + && 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() {
|
| + // If we're not in the browser, we're almost certainly running on v8.
|
| + if (JS("bool", "typeof(navigator) != 'object'")) return chromeTypeNameOf;
|
| +
|
| + String userAgent = JS('String', "navigator.userAgent");
|
| + if (userAgent.contains(const RegExp('Chrome|DumpRenderTree'))) {
|
| + return chromeTypeNameOf;
|
| + } else if (userAgent.contains(const RegExp('Firefox'))) {
|
| + return firefoxTypeNameOf;
|
| + } else if (userAgent.contains('MSIE')) {
|
| + return ieTypeNameOf;
|
| + } else {
|
| + return constructorNameWithFallback;
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Cached value for the function to use to get the type name of an
|
| + * object.
|
| + */
|
| +Function _getTypeNameOf;
|
| +
|
| +/**
|
| + * 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);
|
| +}
|
| +
|
| +/**
|
| + * Returns a method that queries [methods] with the prototype of [obj],
|
| + * patches the prototype of [obj] with the found JS code,
|
| + * and invokes the JS code.
|
| + */
|
| +dynamicBind(var obj,
|
| + String name,
|
| + var methods,
|
| + List arguments) {
|
| + String tag = getTypeNameOf(obj);
|
| + var method = JS('var', '#[#]', methods, tag);
|
| +
|
| + if (method === null && _dynamicMetadata !== null) {
|
| + 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));'
|
| + '}',
|
| + JS_TO_CLOSURE(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)) {
|
| + 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);
|
| + if (dartMethod !== null) JS('void', "#['Object'] = #", methods, dartMethod);
|
| +
|
| + var bind = JS('var',
|
| + 'function() {'
|
| + 'return #(this, #, #, Array.prototype.slice.call(arguments));'
|
| + '}',
|
| + JS_TO_CLOSURE(dynamicBind), name, methods);
|
| +
|
| + JS('void', '#.methods = #', bind, methods);
|
| + defineProperty(JS('var', 'Object.prototype'), name, bind);
|
| + return methods;
|
| +}
|
| +
|
| +
|
| +class MetaInfo {
|
| + String tag;
|
| + String tags;
|
| + Set<String> set;
|
| + MetaInfo(this.tag, this.tags, this.set);
|
| +}
|
| +
|
| +List<MetaInfo> get _dynamicMetadata() {
|
| + return JS('var', '\$dynamicMetadata');
|
| +}
|
| +
|
| +void set _dynamicMetadata(List<String> table) {
|
| + return JS('void', '\$dynamicMetadata = #', table);
|
| +}
|
| +
|
| +/**
|
| + * Builds the metadata used for encoding the class hierarchy of native
|
| + * classes.
|
| + */
|
| +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));
|
| + }
|
| +}
|
| +
|
| +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;
|
| +}
|
|
|