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

Unified Diff: frog/leg/native_emitter.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/native_emitter.dart
===================================================================
--- frog/leg/native_emitter.dart (revision 5866)
+++ frog/leg/native_emitter.dart (working copy)
@@ -5,10 +5,7 @@
class NativeEmitter {
Compiler compiler;
- bool addedDynamicFunction = false;
- bool addedTypeNameOfFunction = false;
- bool addedDefPropFunction = false;
- bool addedNativeProperty = false;
+ StringBuffer buffer;
// Classes that participate in dynamic dispatch. These are the
// classes that contain used members.
@@ -30,239 +27,40 @@
: classesWithDynamicDispatch = new Set<ClassElement>(),
nativeClasses = new Set<ClassElement>(),
subtypes = new Map<ClassElement, List<ClassElement>>(),
- overriddenMethods = new Set<FunctionElement>();
+ overriddenMethods = new Set<FunctionElement>(),
+ buffer = new StringBuffer();
- /**
- * Code for finding the type name of a JavaScript object.
- */
- static final String TYPE_NAME_OF_FUNCTION = @"""
-(function() {
- function constructorNameWithFallback(obj) {
- var constructor = obj.constructor;
- if (typeof(constructor) == 'function') {
- // The constructor isn't null or undefined at this point. Try
- // to grab hold of its name.
- var name = constructor.name;
- // 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 (typeof(name) == 'string' && name && name != 'Object') return name;
- }
- var string = Object.prototype.toString.call(obj);
- return string.substring(8, string.length - 1);
+ String get dynamicName() {
+ Element element = compiler.findHelper(
+ const SourceString('dynamicFunction'));
+ return compiler.namer.isolateAccess(element);
}
- function chrome$typeNameOf(obj) {
- var name = obj.constructor.name;
- if (name == 'Window') return 'DOMWindow';
- return name;
+ String get dynamicSetMetadataName() {
+ Element element = compiler.findHelper(
+ const SourceString('dynamicSetMetadata'));
+ return compiler.namer.isolateAccess(element);
}
- function firefox$typeNameOf(obj) {
- var name = constructorNameWithFallback(obj);
- if (name == 'Window') return 'DOMWindow';
- if (name == 'Document') return 'HTMLDocument';
- if (name == 'XMLDocument') return 'Document';
- return name;
+ String get typeNameOfName() {
+ Element element = compiler.findHelper(
+ const SourceString('getTypeNameOf'));
+ return compiler.namer.isolateAccess(element);
}
- function ie$typeNameOf(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' && obj.xmlVersion) return 'Document';
- if (name == 'Document') return 'HTMLDocument';
- return name;
+ String get dynamicIsCheckName() {
+ Element element = compiler.findHelper(
+ const SourceString('dynamicIsCheck'));
+ return compiler.namer.isolateAccess(element);
}
- // If we're not in the browser, we're almost certainly running on v8.
- if (typeof(navigator) != 'object') return chrome$typeNameOf;
-
- var userAgent = navigator.userAgent;
- if (/Chrome|DumpRenderTree/.test(userAgent)) return chrome$typeNameOf;
- if (/Firefox/.test(userAgent)) return firefox$typeNameOf;
- if (/MSIE/.test(userAgent)) return ie$typeNameOf;
- return constructorNameWithFallback;
-})()""";
-
- /**
- * Code for defining a property in a JavaScript object that will not
- * be visible through for-in (aka enumerable is false).
- */
- static final String DEF_PROP_FUNCTION = '''
-function(obj, prop, value) {
- Object.defineProperty(obj, prop,
- {value: value, enumerable: false, writable: true, configurable: true});
-}''';
-
- /**
- * 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:
- *
- * $dynamic('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 a function that queries the
- * map with the prototype of 'this', patches the prototype of
- * 'this' with the found JS code, and invokes the JS code.
- *
- */
- String buildDynamicFunctionCode() {
- ClassElement noSuchMethodException =
- compiler.coreLibrary.find(Compiler.NO_SUCH_METHOD_EXCEPTION);
- Element helper = compiler.findHelper(new SourceString('captureStackTrace'));
- String capture = compiler.namer.isolateAccess(helper);
- String exception = compiler.namer.isolateAccess(noSuchMethodException);
-
- return '''
-function(name) {
- var f = Object.prototype[name];
- if (f && f.methods) return f.methods;
-
- var methods = {};
- // 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 = ${compiler.emitter.objectClassName}.prototype[name];
- if (dartMethod) methods.Object = dartMethod;
- function dynamicBind() {
- // Find the target method
- var obj = this;
- var tag = $typeNameOfName(obj);
- var method = methods[tag];
- if (!method) {
- var table = $dynamicMetadataName;
- for (var i = 0; i < table.length; i++) {
- var entry = table[i];
- if (entry.map.hasOwnProperty(tag)) {
- method = methods[entry.tag];
- if (method) break;
- }
- }
- }
- method = method || methods.Object;
-
- if (method == null) {
- method = function() {
- throw $capture(new $exception(obj, name, arguments));
- };
- }
-
- var proto = Object.getPrototypeOf(obj);
- var nullCheckMethod = function() {
- var res = method.apply(this, Array.prototype.slice.call(arguments));
- return res === null ? (void 0) : res;
- }
- if (!proto.hasOwnProperty(name)) {
- $defPropName(proto, name, nullCheckMethod);
- }
-
- return nullCheckMethod.apply(this, Array.prototype.slice.call(arguments));
- };
- dynamicBind.methods = methods;
- $defPropName(Object.prototype, name, dynamicBind);
- return methods;
-}''';
-}
-
- String buildDynamicMetadataCode() => '''
-if (typeof $dynamicMetadataName == 'undefined') $dynamicMetadataName = [];''';
-
- // 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]).
- String buildDynamicIsCheckCode() {
- ClassElement objectClass =
- compiler.coreLibrary.find(const SourceString('Object'));
- return '''
-function(obj, isCheck) {
- if (obj.constructor === Array) return false;
- var proto = Object.getPrototypeOf(obj);
- // Check if the Dart object corresponding to this class has the property.
- var res =
- !!${compiler.namer.CURRENT_ISOLATE}.native[$typeNameOfName(obj)][isCheck];
- res = res || false;
- $defPropName(proto, isCheck, res);
- return res;
-}''';
+ String get isChecksHelperName() {
+ Element element = compiler.findHelper(
+ const SourceString('isChecksHelper'));
+ return compiler.namer.isolateAccess(element);
}
- String buildNativePropertyCode() => '''
-${compiler.namer.ISOLATE}.prototype.native = {};''';
-
- String buildDynamicSetMetadataCode() => """
-function(inputTable) {
- // TODO: Deal with light isolates.
- var table = [];
- for (var i = 0; i < inputTable.length; i++) {
- var tag = inputTable[i][0];
- var tags = inputTable[i][1];
- var map = {};
- var tagNames = tags.split('|');
- for (var j = 0; j < tagNames.length; j++) {
- map[tagNames[j]] = true;
- }
- table.push({tag: tag, tags: tags, map: map});
- }
- $dynamicMetadataName = table;
-}""";
-
-
- String get dynamicName() => '${compiler.namer.ISOLATE}.\$dynamic';
- String get defPropName() => '${compiler.namer.ISOLATE}.\$defProp';
- String get typeNameOfName() => '${compiler.namer.ISOLATE}.\$typeNameOf';
- String get dynamicMetadataName() =>
- '${compiler.namer.ISOLATE}.\$dynamicMetatada';
- String get dynamicIsCheckName() =>
- '${compiler.namer.ISOLATE}.\$dynamicIsCheck';
- String get dynamicSetMetadataName() =>
- '${compiler.namer.ISOLATE}.\$dynamicSetMetatada';
-
- void addDynamicFunctionIfNecessary(StringBuffer buffer) {
- if (addedDynamicFunction) return;
- addedDynamicFunction = true;
- addTypeNameOfFunctionIfNecessary(buffer);
- buffer.add('$dynamicName = ');
- buffer.add(buildDynamicFunctionCode());
- buffer.add('\n');
- buffer.add(buildDynamicMetadataCode());
- buffer.add('\n');
- }
-
- void addTypeNameOfFunctionIfNecessary(StringBuffer buffer) {
- if (addedTypeNameOfFunction) return;
- addedTypeNameOfFunction = true;
- addDefPropFunctionIfNecessary(buffer);
- buffer.add('$typeNameOfName = ');
- buffer.add(TYPE_NAME_OF_FUNCTION);
- buffer.add('\n');
- }
-
- void addDefPropFunctionIfNecessary(StringBuffer buffer) {
- if (addedDefPropFunction) return;
- addedDefPropFunction = true;
- buffer.add('$defPropName = ');
- buffer.add(DEF_PROP_FUNCTION);
- buffer.add('\n');
- }
-
- void addNativePropertyIfNecessary(StringBuffer buffer) {
- if (addedNativeProperty) return;
- addedNativeProperty = true;
- buffer.add(buildNativePropertyCode());
- buffer.add('\n');
- }
-
- void generateNativeLiteral(ClassElement classElement, StringBuffer buffer) {
+ void generateNativeLiteral(ClassElement classElement) {
String quotedNative = classElement.nativeName.slowToString();
String nativeCode = quotedNative.substring(2, quotedNative.length - 1);
String className = compiler.namer.getName(classElement);
@@ -299,13 +97,13 @@
}
}
- void generateNativeClass(ClassElement classElement, StringBuffer buffer) {
+ void generateNativeClass(ClassElement classElement) {
nativeClasses.add(classElement);
assert(classElement.backendMembers.isEmpty());
String quotedName = classElement.nativeName.slowToString();
if (isNativeLiteral(quotedName)) {
- generateNativeLiteral(classElement, buffer);
+ generateNativeLiteral(classElement);
// The native literal kind needs to be dealt with specially when
// generating code for it.
return;
@@ -316,7 +114,6 @@
String attachTo(String name) {
hasUsedSelectors = true;
- addDynamicFunctionIfNecessary(buffer);
return "$dynamicName('$name').$nativeName";
}
@@ -327,10 +124,8 @@
}
}
- addNativePropertyIfNecessary(buffer);
- // Create an object that contains the is checks properties. The
- // object will be used when entering [buildDynamicIsCheckCode].
- buffer.add('${compiler.namer.ISOLATE}.prototype.native.$nativeName = { ');
+ // Create an object that contains the is checks properties.
+ buffer.add('$isChecksHelperName.$nativeName = { ');
List<String> tests = <String>[];
ClassElement objectClass =
@@ -362,8 +157,7 @@
String invocationName,
String stubParameters,
List<String> argumentsBuffer,
- int indexOfLastOptionalArgumentInParameters,
- StringBuffer buffer) {
+ int indexOfLastOptionalArgumentInParameters) {
// The target JS function may check arguments.length so we need to
// make sure not to pass any unspecified optional arguments to it.
// For example, for the following Dart method:
@@ -404,13 +198,7 @@
buffer.add(');');
}
- void emitDynamicDispatchMetadata(StringBuffer buffer) {
- // TODO(ngeoffray): emit this conditionally.
- addTypeNameOfFunctionIfNecessary(buffer);
- buffer.add('$dynamicIsCheckName = ');
- buffer.add(buildDynamicIsCheckCode());
- buffer.add('\n');
-
+ void emitDynamicDispatchMetadata() {
if (classesWithDynamicDispatch.isEmpty()) return;
buffer.add('// ${classesWithDynamicDispatch.length} dynamic classes.\n');
@@ -502,10 +290,6 @@
// Write out a thunk that builds the metadata.
if (!tagDefns.isEmpty()) {
- buffer.add('$dynamicSetMetadataName = ');
- buffer.add(buildDynamicSetMetadataCode());
- buffer.add(';\n\n');
-
buffer.add('(function(){\n');
for (final String varName in varNames) {
@@ -529,4 +313,8 @@
buffer.add('})();\n');
}
}
+
+ void assembleCode(StringBuffer other) {
+ other.add('(function() { $isChecksHelperName = {};\n$buffer\n })();\n');
+ }
}

Powered by Google App Engine
This is Rietveld 408576698