| 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);
|
|
|