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

Unified Diff: frog/leg/emitter.dart

Issue 9418045: Support for native in leg, and start moving native tests to a specific test suite. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 10 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/emitter.dart
===================================================================
--- frog/leg/emitter.dart (revision 4390)
+++ frog/leg/emitter.dart (working copy)
@@ -21,13 +21,114 @@
}
}''';
+ /**
+ * Code for finding the type name of a JavaScript object.
+ */
+ static final String TYPE_NAME_OF_FUNCTION = '''
+function(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 (name && typeof(name) == 'string' && name != 'Object') return name;
+ }
+ var string = Object.prototype.toString.call(obj);
+ var name = string.substring(8, string.length - 1);
+ if (name == 'Window') {
+ name = 'DOMWindow';
+ } else if (name == 'Document') {
+ name = 'HTMLDocument';
+ }
+ return name;
+}
+''';
+
+ /**
+ * 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() => '''
+function(name) {
+ var f = Object.prototype[name];
+ if (f && f.methods) return f.methods;
+
+ var methods = {};
+ if (f) methods.Object = f;
+ 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;
+ var proto = Object.getPrototypeOf(obj);
+ if (!proto.hasOwnProperty(name)) {
+ $defPropName(proto, name, method);
+ }
+
+ return method.apply(this, Array.prototype.slice.call(arguments));
+ };
+ dynamicBind.methods = methods;
+ $defPropName(Object.prototype, name, dynamicBind);
+ return methods;
+}
+''';
+
+ String buildDynamicMetadataCode() => '''
+if (typeof $dynamicMetadataName == 'undefined') $dynamicMetadataName = [];
+''';
+
bool addedInheritFunction = false;
+ bool addedDynamicFunction = false;
final Namer namer;
CodeEmitterTask(Compiler compiler) : namer = compiler.namer, super(compiler);
String get name() => 'CodeEmitter';
String get inheritsName() => '${compiler.namer.ISOLATE}.\$inherits';
+ 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';
void addInheritFunctionIfNecessary(StringBuffer buffer) {
if (addedInheritFunction) return;
@@ -37,8 +138,24 @@
buffer.add(';\n');
}
+ void addDynamicFunctionIfNecessary(StringBuffer buffer) {
+ if (addedDynamicFunction) return;
+ addedDynamicFunction = true;
+ buffer.add('$defPropName = ');
+ buffer.add(DEF_PROP_FUNCTION);
+ buffer.add('\n');
+ buffer.add('$typeNameOfName = ');
+ buffer.add(TYPE_NAME_OF_FUNCTION);
+ buffer.add('\n');
+ buffer.add('$dynamicName = ');
+ buffer.add(buildDynamicFunctionCode());
+ buffer.add('\n');
+ buffer.add(buildDynamicMetadataCode());
+ buffer.add('\n');
+ }
+
void addParameterStub(FunctionElement member,
- String prototype,
+ String attachTo(String invocationName),
StringBuffer buffer,
Selector selector) {
FunctionParameters parameters = member.computeParameters(compiler);
@@ -52,7 +169,7 @@
String invocationName =
namer.instanceMethodInvocationName(member.name, selector);
- buffer.add('$prototype.$invocationName = function(');
+ buffer.add('${attachTo(invocationName)} = function(');
// The parameters that this stub takes.
List<String> parametersBuffer = new List<String>(selector.argumentCount);
@@ -121,13 +238,13 @@
}
void addParameterStubs(FunctionElement member,
- String prototype,
+ String attachTo(String invocationName),
StringBuffer buffer) {
Set<Selector> selectors = compiler.universe.invokedNames[member.name];
if (selectors == null) return;
for (Selector selector in selectors) {
if (!selector.applies(compiler, member)) continue;
- addParameterStub(member, prototype, buffer, selector);
+ addParameterStub(member, attachTo, buffer, selector);
}
}
@@ -140,9 +257,8 @@
|| member.kind === ElementKind.GETTER
|| member.kind === ElementKind.SETTER) {
String codeBlock = compiler.universe.generatedCode[member];
- if (codeBlock !== null) {
- buffer.add('$prototype.${namer.getName(member)} = $codeBlock;\n');
- }
+ if (codeBlock == null) return;
+ buffer.add('$prototype.${namer.getName(member)} = $codeBlock;\n');
codeBlock = compiler.universe.generatedBailoutCode[member];
if (codeBlock !== null) {
String name = namer.getBailoutName(member);
@@ -150,7 +266,7 @@
}
FunctionElement function = member;
if (!function.computeParameters(compiler).optionalParameters.isEmpty()) {
- addParameterStubs(member, prototype, buffer);
+ addParameterStubs(member, (name) => '$prototype.$name', buffer);
}
} else if (member.kind === ElementKind.FIELD) {
// TODO(ngeoffray): Have another class generate the code for the
@@ -186,7 +302,7 @@
String memberName = namer.instanceFieldName(member.name);
argumentsBuffer.add('${className}_$memberName');
bodyBuffer.add(' this.$memberName = ${className}_$memberName;\n');
- }
+ }
}
for (Element element in classElement.members) {
@@ -200,6 +316,54 @@
} while(classElement !== null);
}
+ void generateNativeClass(ClassElement classElement, StringBuffer buffer) {
+ addDynamicFunctionIfNecessary(buffer);
+ assert(classElement.backendMembers.isEmpty());
+ String nativeName = classElement.nativeName.stringValue;
+ nativeName = nativeName.substring(2, nativeName.length - 1);
+ for (Element member in classElement.members) {
+ if (member.isInstanceMember()) {
+ String memberName = namer.getName(member);
+ if (member.kind === ElementKind.FUNCTION
+ || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY
+ || member.kind === ElementKind.GETTER
+ || member.kind === ElementKind.SETTER) {
+ String codeBlock = compiler.universe.generatedCode[member];
+ if (codeBlock == null) continue;
+ buffer.add(
+ "$dynamicName('$memberName').$nativeName = $codeBlock;\n");
+ codeBlock = compiler.universe.generatedBailoutCode[member];
+ if (codeBlock !== null) {
+ String name = namer.getBailoutName(member);
+ buffer.add("$dynamicName('$name').$nativeName = $codeBlock;\n");
+ }
+ FunctionElement function = member;
+ FunctionParameters parameters = function.computeParameters(compiler);
+ if (!parameters.optionalParameters.isEmpty()) {
+ addParameterStubs(
+ member, (name) => "$dynamicName('$name').$nativeName", buffer);
+ }
+ } else if (member.kind === ElementKind.FIELD) {
+ if (compiler.universe.invokedSetters.contains(member.name)) {
+ String setterName = namer.setterName(member.name);
+ buffer.add(
+ "$dynamicName('$setterName').$nativeName = function(v){\n" +
+ ' this.${member.name} = v;\n};\n');
+ }
+ if (compiler.universe.invokedGetters.contains(member.name)) {
+ String getterName = namer.getterName(member.name);
+ buffer.add(
+ "$dynamicName('$getterName').$nativeName = function(){\n" +
+ ' return this.${member.name};\n};\n');
+ }
+ } else {
+ compiler.internalError('unexpected kind: "${member.kind}"',
+ element: member);
+ }
+ }
+ }
+ }
+
void generateClass(ClassElement classElement,
StringBuffer buffer,
Set<ClassElement> seenClasses) {
@@ -210,6 +374,11 @@
generateClass(classElement.superclass, buffer, seenClasses);
}
+ if (classElement.isNative()) {
+ generateNativeClass(classElement, buffer);
+ return;
+ }
+
String className = namer.isolatePropertyAccess(classElement);
buffer.add('$className = function ${classElement.name}(');
StringBuffer bodyBuffer = new StringBuffer();
@@ -301,7 +470,7 @@
String invocationName =
namer.instanceMethodName(callElement.name, parameterCount);
buffer.add("$staticName.$invocationName = $staticName;\n");
- addParameterStubs(callElement, staticName, buffer);
+ addParameterStubs(callElement, (name) => '$staticName.$name', buffer);
}
}
@@ -354,7 +523,7 @@
buffer.add("$prototype.$invocationName = function($joinedArgs) {\n");
buffer.add(" return this.self.$targetName($joinedArgs);\n");
buffer.add("};\n");
- addParameterStubs(callElement, prototype, buffer);
+ addParameterStubs(callElement, (name) => '$prototype.$name', buffer);
// And finally the getter.
String enclosingClassAccess = namer.isolatePropertyAccess(enclosingClass);

Powered by Google App Engine
This is Rietveld 408576698