Chromium Code Reviews| Index: frog/leg/emitter.dart |
| =================================================================== |
| --- frog/leg/emitter.dart (revision 4354) |
| +++ frog/leg/emitter.dart (working copy) |
| @@ -21,13 +21,87 @@ |
| } |
| }'''; |
| + static final String TYPE_NAME_OF_FUNCTION = ''' |
| +function(obj) { |
|
ahe
2012/02/19 14:38:02
Could you add a documentation comment for this fun
ngeoffray
2012/02/20 09:09:37
Sure. For the reference, this code is a pure copy
|
| + var constructor = obj.constructor; |
|
ahe
2012/02/19 14:38:02
What happens if obj is undefined?
ngeoffray
2012/02/20 09:09:37
boom? :)
floitsch
2012/02/21 13:11:15
Note that this function is called on the prototype
|
| + if (typeof(constructor) == 'function') { |
|
floitsch
2012/02/19 00:59:43
typeof is not a function:
if (typeof constructor =
ngeoffray
2012/02/20 09:09:37
Copy/pasted code.
|
| + // 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; |
|
floitsch
2012/02/19 00:59:43
exchange typeof and name-check.
It probably does n
ngeoffray
2012/02/20 09:09:37
Copy-pasted code.
|
| + } |
| + var string = Object.prototype.toString.call(obj); |
| + var name = string.substring(8, string.length - 1); |
|
ahe
2012/02/19 14:38:02
I'm getting a feeling that this is very similar to
ngeoffray
2012/02/20 09:09:37
Not on this code, which is copy-pasted.
|
| + if (name == 'Window') { |
| + name = 'DOMWindow'; |
| + } else if (name == 'Document') { |
| + name = 'HTMLDocument'; |
| + } |
| + return name; |
| +} |
| +'''; |
| + |
| + static final String DEF_PROP_FUNCTION = ''' |
| +function(obj, prop, value) { |
| + Object.defineProperty(obj, prop, |
| + {value: value, enumerable: false, writable: true, configurable: true}); |
| +}'''; |
| + |
| + String get DYNAMIC_FUNCTION() => ''' |
|
floitsch
2012/02/19 00:59:43
this needs comments.
ahe
2012/02/19 14:38:02
Documentation comment, please.
ngeoffray
2012/02/20 09:09:37
As for TYPE_NAME_OF_FUNCTION, this method was take
ahe
2012/02/20 09:36:10
Whatever information you have about these function
|
| +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; |
| +} |
| +if (typeof $dynamicMetadataName == 'undefined') $dynamicMetadataName = []; |
|
ahe
2012/02/19 14:38:02
Extra code after function. Shouldn't this be in th
ngeoffray
2012/02/20 09:09:37
Code also comes from frog.
ahe
2012/02/20 09:36:10
You chose the word "DYNAMIC_FUNCTION". Clearly thi
|
| +'''; |
| + |
| 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'; |
|
ahe
2012/02/19 14:38:02
Why aren't these strings final values? Generating
ngeoffray
2012/02/20 09:09:37
They cannot be final because compiler.namer.ISOLAT
|
| + 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'; |
|
ahe
2012/02/19 14:38:02
How is this related to line 89?
ngeoffray
2012/02/20 09:09:37
Not sure I understand.
ahe
2012/02/20 09:36:10
I overlooked that the code on 89 wasn't in a raw s
|
| void addInheritFunctionIfNecessary(StringBuffer buffer) { |
| if (addedInheritFunction) return; |
| @@ -37,8 +111,22 @@ |
| 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(DYNAMIC_FUNCTION); |
| + buffer.add(';\n'); |
| + } |
| + |
| void addParameterStub(FunctionElement member, |
| - String prototype, |
| + String attachTo(String invocationName), |
| StringBuffer buffer, |
| Selector selector) { |
| FunctionParameters parameters = member.computeParameters(compiler); |
| @@ -52,7 +140,7 @@ |
| String invocationName = |
| namer.instanceMethodInvocationName(member.name, selector); |
| - buffer.add('$prototype.$invocationName = function('); |
| + buffer.add('${attachTo(invocationName)} = function('); |
|
ahe
2012/02/19 14:38:02
How about turning this into a streaming API, that
|
| // The parameters that this stub takes. |
| List<String> parametersBuffer = new List<String>(selector.argumentCount); |
| @@ -121,13 +209,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); |
| } |
| } |
| @@ -150,7 +238,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 +274,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 +288,55 @@ |
| } while(classElement !== null); |
| } |
| + void generateNativeClass(ClassElement classElement, StringBuffer buffer) { |
| + addDynamicFunctionIfNecessary(buffer); |
| + assert(classElement.backendMembers.isEmpty()); |
| + String nativeName = classElement.nativeName.substring( |
| + 2, classElement.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) { |
| + buffer.add( |
| + "$dynamicName('$memberName').$nativeName = $codeBlock;\n"); |
| + } |
| + codeBlock = compiler.universe.generatedBailoutCode[member]; |
| + if (codeBlock !== null) { |
|
floitsch
2012/02/19 00:59:43
Maybe we should consider making our bailout functi
|
| + String name = namer.getBailoutName(member); |
| + buffer.add("$dynamicName('$name').$nativeName = $codeBlock;\n"); |
| + } |
| + FunctionElement function = member; |
| + FunctionParameters parameters = function.computeParameters(compiler); |
| + if (!parameters.optionalParameters.isEmpty()) { |
|
ahe
2012/02/19 14:38:02
Is this necessary if there is no code generated?
ngeoffray
2012/02/20 09:09:37
Very good catch. Will remove it.
|
| + 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 +347,9 @@ |
| generateClass(classElement.superclass, buffer, seenClasses); |
| } |
| + if (classElement.isNative()) { |
| + return generateNativeClass(classElement, buffer); |
|
floitsch
2012/02/19 00:59:43
don't use 'return val' to leave a void function.
|
| + } |
| String className = namer.isolatePropertyAccess(classElement); |
| buffer.add('$className = function ${classElement.name}('); |
| StringBuffer bodyBuffer = new StringBuffer(); |
| @@ -290,7 +430,7 @@ |
| String invocationName = |
| namer.instanceMethodName(callElement.name, parameterCount); |
| buffer.add("$staticName.$invocationName = $staticName;\n"); |
| - addParameterStubs(callElement, staticName, buffer); |
| + addParameterStubs(callElement, (name) => '$staticName.$name', buffer); |
| } |
| } |
| @@ -343,7 +483,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); |