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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart

Issue 11602016: Emit classes using ASTs (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of js_backend; 5 part of js_backend;
6 6
7 class NativeEmitter { 7 class NativeEmitter {
8 8
9 CodeEmitterTask emitter; 9 CodeEmitterTask emitter;
10 CodeBuffer nativeBuffer; 10 CodeBuffer nativeBuffer;
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 String quotedName = cls.nativeTagInfo.slowToString(); 118 String quotedName = cls.nativeTagInfo.slowToString();
119 if (isNativeGlobal(quotedName)) { 119 if (isNativeGlobal(quotedName)) {
120 // Global object, just be like the other types for now. 120 // Global object, just be like the other types for now.
121 return quotedName.substring(3, quotedName.length - 1); 121 return quotedName.substring(3, quotedName.length - 1);
122 } else { 122 } else {
123 return quotedName.substring(2, quotedName.length - 1); 123 return quotedName.substring(2, quotedName.length - 1);
124 } 124 }
125 } 125 }
126 126
127 void generateNativeClass(ClassElement classElement) { 127 void generateNativeClass(ClassElement classElement) {
128 assert(classElement.backendMembers.isEmpty);
128 nativeClasses.add(classElement); 129 nativeClasses.add(classElement);
129 130
130 assert(classElement.backendMembers.isEmpty); 131 ClassBuilder builder = new ClassBuilder();
131 String quotedName = classElement.nativeTagInfo.slowToString(); 132 emitter.emitClassFields(classElement, builder, classIsNative: true);
133 emitter.emitClassGettersSetters(classElement, builder);
134 emitter.emitInstanceMembers(classElement, builder);
132 135
133 CodeBuffer fieldBuffer = new CodeBuffer(); 136 // An empty native class may be omitted since the superclass methods can be
134 CodeBuffer getterSetterBuffer = new CodeBuffer(); 137 // located via the dispatch metadata.
135 CodeBuffer methodBuffer = new CodeBuffer(); 138 if (builder.properties.isEmpty) return;
136
137 emitter.emitClassFields(classElement, fieldBuffer, false,
138 classIsNative: true);
139 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false);
140 emitter.emitInstanceMembers(classElement, methodBuffer, false);
141
142 if (methodBuffer.isEmpty
143 && fieldBuffer.isEmpty
144 && getterSetterBuffer.isEmpty) {
145 return;
146 }
147 139
148 String nativeTag = toNativeTag(classElement); 140 String nativeTag = toNativeTag(classElement);
149 nativeBuffer.add("$defineNativeClassName('$nativeTag',$_"); 141 js.Expression definition =
150 nativeBuffer.add('{'); 142 js.call(js.use(defineNativeClassName),
151 bool firstInMap = true; 143 [js.string(nativeTag), builder.toObjectInitializer()]);
152 if (!fieldBuffer.isEmpty) { 144
153 firstInMap = false; 145 nativeBuffer.add(js.prettyPrint(definition, compiler));
154 nativeBuffer.add(fieldBuffer); 146 nativeBuffer.add('$N$n');
155 }
156 if (!getterSetterBuffer.isEmpty) {
157 if (!firstInMap) nativeBuffer.add(",");
158 firstInMap = false;
159 nativeBuffer.add("\n$_");
160 nativeBuffer.add(getterSetterBuffer);
161 }
162 if (!methodBuffer.isEmpty) {
163 if (!firstInMap) nativeBuffer.add(",");
164 nativeBuffer.add(methodBuffer);
165 }
166 nativeBuffer.add('$n})$N$n');
167 147
168 classesWithDynamicDispatch.add(classElement); 148 classesWithDynamicDispatch.add(classElement);
169 } 149 }
170 150
171 List<ClassElement> getDirectSubclasses(ClassElement cls) { 151 List<ClassElement> getDirectSubclasses(ClassElement cls) {
172 List<ClassElement> result = directSubtypes[cls]; 152 List<ClassElement> result = directSubtypes[cls];
173 return result == null ? const<ClassElement>[] : result; 153 return result == null ? const<ClassElement>[] : result;
174 } 154 }
175 155
176 void potentiallyConvertDartClosuresToJs(List<js.Statement> statements, 156 void potentiallyConvertDartClosuresToJs(List<js.Statement> statements,
(...skipping 12 matching lines...) Expand all
189 for (js.Parameter stubParameter in stubParameters) { 169 for (js.Parameter stubParameter in stubParameters) {
190 if (stubParameter.name == name) { 170 if (stubParameter.name == name) {
191 DartType type = parameter.computeType(compiler).unalias(compiler); 171 DartType type = parameter.computeType(compiler).unalias(compiler);
192 if (type is FunctionType) { 172 if (type is FunctionType) {
193 // The parameter type is a function type either directly or through 173 // The parameter type is a function type either directly or through
194 // typedef(s). 174 // typedef(s).
195 int arity = type.computeArity(); 175 int arity = type.computeArity();
196 176
197 statements.add( 177 statements.add(
198 new js.ExpressionStatement( 178 new js.ExpressionStatement(
199 new js.Assignment( 179 js.assign(
200 new js.VariableUse(name), 180 js.use(name),
201 new js.VariableUse(closureConverter) 181 js.use(closureConverter).callWith(
202 .callWith([new js.VariableUse(name), 182 [js.use(name), new js.LiteralNumber('$arity')]))));
203 new js.LiteralNumber('$arity')]))));
204 break; 183 break;
205 } 184 }
206 } 185 }
207 } 186 }
208 }); 187 });
209 } 188 }
210 189
211 List<js.Statement> generateParameterStubStatements( 190 List<js.Statement> generateParameterStubStatements(
212 Element member, 191 Element member,
213 String invocationName, 192 String invocationName,
214 List<js.Parameter> stubParameters, 193 List<js.Parameter> stubParameters,
215 List<js.Expression> argumentsBuffer, 194 List<js.Expression> argumentsBuffer,
216 int indexOfLastOptionalArgumentInParameters) { 195 int indexOfLastOptionalArgumentInParameters) {
217 // The target JS function may check arguments.length so we need to 196 // The target JS function may check arguments.length so we need to
218 // make sure not to pass any unspecified optional arguments to it. 197 // make sure not to pass any unspecified optional arguments to it.
219 // For example, for the following Dart method: 198 // For example, for the following Dart method:
220 // foo([x, y, z]); 199 // foo([x, y, z]);
221 // The call: 200 // The call:
222 // foo(y: 1) 201 // foo(y: 1)
223 // must be turned into a JS call to: 202 // must be turned into a JS call to:
224 // foo(null, y). 203 // foo(null, y).
225 204
226 ClassElement classElement = member.enclosingElement; 205 ClassElement classElement = member.enclosingElement;
227 //String nativeTagInfo = classElement.nativeName.slowToString();
228 String nativeTagInfo = classElement.nativeTagInfo.slowToString(); 206 String nativeTagInfo = classElement.nativeTagInfo.slowToString();
229 207
230 List<js.Statement> statements = <js.Statement>[]; 208 List<js.Statement> statements = <js.Statement>[];
231 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); 209 potentiallyConvertDartClosuresToJs(statements, member, stubParameters);
232 210
233 String target; 211 String target;
234 List<js.Expression> arguments; 212 List<js.Expression> arguments;
235 213
236 if (!nativeMethods.contains(member)) { 214 if (!nativeMethods.contains(member)) {
237 // When calling a method that has a native body, we call it with our 215 // When calling a method that has a native body, we call it with our
(...skipping 23 matching lines...) Expand all
261 239
262 // If a method is overridden, we must check if the prototype of 'this' has the 240 // If a method is overridden, we must check if the prototype of 'this' has the
263 // method available. Otherwise, we may end up calling the method from the 241 // method available. Otherwise, we may end up calling the method from the
264 // super class. If the method is not available, we make a direct call to 242 // super class. If the method is not available, we make a direct call to
265 // Object.prototype.$methodName. This method will patch the prototype of 243 // Object.prototype.$methodName. This method will patch the prototype of
266 // 'this' to the real method. 244 // 'this' to the real method.
267 js.Statement generateMethodBodyWithPrototypeCheck( 245 js.Statement generateMethodBodyWithPrototypeCheck(
268 String methodName, 246 String methodName,
269 js.Statement body, 247 js.Statement body,
270 List<js.Parameter> parameters) { 248 List<js.Parameter> parameters) {
271 return new js.If( 249 return js.if_(
272 new js.VariableUse('Object') 250 js.use('Object').dot('getPrototypeOf')
273 .dot('getPrototypeOf') 251 .callWith([js.use('this')])
274 .callWith([new js.VariableUse('this')]) 252 .dot('hasOwnProperty').callWith([js.string(methodName)]),
275 .dot('hasOwnProperty')
276 .callWith([new js.LiteralString("'$methodName'")]),
277 body, 253 body,
278 new js.Block( 254 js.return_(
279 <js.Statement>[ 255 js.use('Object').dot('prototype').dot(methodName).dot('call')
280 new js.Return( 256 .callWith(
281 new js.VariableUse('Object') 257 <js.Expression>[js.use('this')]..addAll(
282 .dot('prototype').dot(methodName).dot('call') 258 parameters.map((param) => js.use(param.name))))));
283 .callWith(
284 <js.Expression>[new js.VariableUse('this')]
285 ..addAll(parameters.map((param) =>
286 new js.VariableUse(param.name)))))
287 ]));
288 } 259 }
289 260
290 js.Block generateMethodBodyWithPrototypeCheckForElement( 261 js.Block generateMethodBodyWithPrototypeCheckForElement(
291 FunctionElement element, 262 FunctionElement element,
292 js.Block body, 263 js.Block body,
293 List<js.Parameter> parameters) { 264 List<js.Parameter> parameters) {
294 String methodName; 265 String methodName;
295 Namer namer = backend.namer; 266 Namer namer = backend.namer;
296 if (element.kind == ElementKind.FUNCTION) { 267 if (element.kind == ElementKind.FUNCTION) {
297 methodName = namer.instanceMethodName(element); 268 methodName = namer.instanceMethodName(element);
298 } else if (element.kind == ElementKind.GETTER) { 269 } else if (element.kind == ElementKind.GETTER) {
299 methodName = namer.getterName(element.getLibrary(), element.name); 270 methodName = namer.getterName(element.getLibrary(), element.name);
300 } else if (element.kind == ElementKind.SETTER) { 271 } else if (element.kind == ElementKind.SETTER) {
301 methodName = namer.setterName(element.getLibrary(), element.name); 272 methodName = namer.setterName(element.getLibrary(), element.name);
302 } else { 273 } else {
303 compiler.internalError('unexpected kind: "${element.kind}"', 274 compiler.internalError("unexpected kind: '${element.kind}'",
304 element: element); 275 element: element);
305 } 276 }
306 277
307 return new js.Block( 278 return new js.Block(
308 [generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]); 279 [generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]);
309 } 280 }
310 281
311 282
312 void emitDynamicDispatchMetadata() { 283 void emitDynamicDispatchMetadata() {
313 if (classesWithDynamicDispatch.isEmpty) return; 284 if (classesWithDynamicDispatch.isEmpty) return;
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 varDefns[varName] = existing; 364 varDefns[varName] = existing;
394 tagDefns[tag] = new js.VariableUse(varName); 365 tagDefns[tag] = new js.VariableUse(varName);
395 expressions.add(new js.VariableUse(varName)); 366 expressions.add(new js.VariableUse(varName));
396 } 367 }
397 } 368 }
398 } 369 }
399 } 370 }
400 walk(classElement); 371 walk(classElement);
401 372
402 if (!subtags.isEmpty) { 373 if (!subtags.isEmpty) {
403 expressions.add( 374 expressions.add(js.string(Strings.join(subtags, '|')));
404 new js.LiteralString("'${Strings.join(subtags, '|')}'"));
405 } 375 }
406 js.Expression expression; 376 js.Expression expression;
407 if (expressions.length == 1) { 377 if (expressions.length == 1) {
408 expression = expressions[0]; 378 expression = expressions[0];
409 } else { 379 } else {
410 js.Expression array = new js.ArrayInitializer.from(expressions); 380 js.Expression array = new js.ArrayInitializer.from(expressions);
411 expression = new js.Call( 381 expression = js.call(array.dot('join'), [js.string('|')]);
412 new js.PropertyAccess.field(array, 'join'),
413 [new js.LiteralString("'|'")]);
414 } 382 }
415 return expression; 383 return expression;
416 } 384 }
417 385
418 for (final ClassElement classElement in preorderDispatchClasses) { 386 for (final ClassElement classElement in preorderDispatchClasses) {
419 tagDefns[classElement] = makeExpression(classElement); 387 tagDefns[classElement] = makeExpression(classElement);
420 } 388 }
421 389
422 // Write out a thunk that builds the metadata. 390 // Write out a thunk that builds the metadata.
423 if (!tagDefns.isEmpty) { 391 if (!tagDefns.isEmpty) {
(...skipping 14 matching lines...) Expand all
438 } 406 }
439 407
440 // [table] is a list of lists, each inner list of the form: 408 // [table] is a list of lists, each inner list of the form:
441 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] 409 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag]
442 // E.g. 410 // E.g.
443 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] 411 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...]
444 js.Expression table = 412 js.Expression table =
445 new js.ArrayInitializer.from( 413 new js.ArrayInitializer.from(
446 preorderDispatchClasses.map((cls) => 414 preorderDispatchClasses.map((cls) =>
447 new js.ArrayInitializer.from([ 415 new js.ArrayInitializer.from([
448 new js.LiteralString("'${toNativeTag(cls)}'"), 416 js.string(toNativeTag(cls)),
449 tagDefns[cls]]))); 417 tagDefns[cls]])));
450 418
451 // $.dynamicSetMetadata(table); 419 // $.dynamicSetMetadata(table);
452 statements.add( 420 statements.add(
453 new js.ExpressionStatement( 421 new js.ExpressionStatement(
454 new js.Call( 422 new js.Call(
455 new js.VariableUse(dynamicSetMetadataName), 423 new js.VariableUse(dynamicSetMetadataName),
456 [table]))); 424 [table])));
457 425
458 // (function(){statements})(); 426 // (function(){statements})();
(...skipping 25 matching lines...) Expand all
484 return subtypes[element] != null; 452 return subtypes[element] != null;
485 } 453 }
486 454
487 bool requiresNativeIsCheck(Element element) { 455 bool requiresNativeIsCheck(Element element) {
488 if (!element.isClass()) return false; 456 if (!element.isClass()) return false;
489 ClassElement cls = element; 457 ClassElement cls = element;
490 if (cls.isNative()) return true; 458 if (cls.isNative()) return true;
491 return isSupertypeOfNativeClass(element); 459 return isSupertypeOfNativeClass(element);
492 } 460 }
493 461
494 void emitIsChecks(Map<String, String> objectProperties) {
495 for (Element element in emitter.checkedClasses) {
496 if (!requiresNativeIsCheck(element)) continue;
497 if (element.isObject(compiler)) continue;
498 String name = backend.namer.operatorIs(element);
499 objectProperties[name] = 'function()$_{${_}return false;$_}';
500 }
501 }
502
503 void assembleCode(CodeBuffer targetBuffer) { 462 void assembleCode(CodeBuffer targetBuffer) {
504 if (nativeClasses.isEmpty) return; 463 if (nativeClasses.isEmpty) return;
505 emitDynamicDispatchMetadata(); 464 emitDynamicDispatchMetadata();
506 targetBuffer.add('$defineNativeClassName = ' 465 targetBuffer.add('$defineNativeClassName = '
507 '$defineNativeClassFunction$N$n'); 466 '$defineNativeClassFunction$N$n');
508 467
468 List<js.Property> objectProperties = <js.Property>[];
469
470 void addProperty(String name, js.Expression value) {
471 objectProperties.add(new js.Property(js.string(name), value));
472 }
473
509 // Because of native classes, we have to generate some is checks 474 // Because of native classes, we have to generate some is checks
510 // by calling a method, instead of accessing a property. So we 475 // by calling a method, instead of accessing a property. So we
511 // attach to the JS Object prototype these methods that return 476 // attach to the JS Object prototype these methods that return
512 // false, and will be overridden by subclasses when they have to 477 // false, and will be overridden by subclasses when they have to
513 // return true. 478 // return true.
514 Map<String, String> objectProperties = new Map<String, String>(); 479 void emitIsChecks() {
515 emitIsChecks(objectProperties); 480 for (Element element in
481 Elements.sortedByPosition(emitter.checkedClasses)) {
482 if (!requiresNativeIsCheck(element)) continue;
483 if (element.isObject(compiler)) continue;
484 String name = backend.namer.operatorIs(element);
485 addProperty(name,
486 js.fun([], js.block1(js.return_(new js.LiteralBool(false)))));
487 }
488 }
489 emitIsChecks();
490
491 js.Expression makeCallOnThis(String functionName) =>
492 js.fun([],
493 js.block1(
494 js.return_(
495 js.call(js.use(functionName), [js.use('this')]))));
516 496
517 // In order to have the toString method on every native class, 497 // In order to have the toString method on every native class,
518 // we must patch the JS Object prototype with a helper method. 498 // we must patch the JS Object prototype with a helper method.
519 String toStringName = backend.namer.publicInstanceMethodNameByArity( 499 String toStringName = backend.namer.publicInstanceMethodNameByArity(
520 const SourceString('toString'), 0); 500 const SourceString('toString'), 0);
521 objectProperties[toStringName] = 501 addProperty(toStringName, makeCallOnThis(toStringHelperName));
522 'function() { return $toStringHelperName(this); }';
523 502
524 // Same as above, but for hashCode. 503 // Same as above, but for hashCode.
525 String hashCodeName = 504 String hashCodeName =
526 backend.namer.publicGetterName(const SourceString('hashCode')); 505 backend.namer.publicGetterName(const SourceString('hashCode'));
527 objectProperties[hashCodeName] = 506 addProperty(hashCodeName, makeCallOnThis(hashCodeHelperName));
528 'function() { return $hashCodeHelperName(this); }';
529 507
530 // If the native emitter has been asked to take care of the 508 // If the native emitter has been asked to take care of the
531 // noSuchMethod handlers, we do that now. 509 // noSuchMethod handlers, we do that now.
532 if (handleNoSuchMethod) { 510 if (handleNoSuchMethod) {
533 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) { 511 emitter.emitNoSuchMethodHandlers(addProperty);
534 objectProperties[name] = buffer.toString();
535 });
536 } 512 }
537 513
538 // If we have any properties to add to Object.prototype, we run 514 // If we have any properties to add to Object.prototype, we run
539 // through them and add them using defineProperty. 515 // through them and add them using defineProperty.
540 if (!objectProperties.isEmpty) { 516 if (!objectProperties.isEmpty) {
541 if (emitter.compiler.enableMinification) targetBuffer.add(";"); 517 js.Expression init =
542 targetBuffer.add("(function(table) {\n" 518 js.call(
543 " for (var key in table) {\n" 519 js.fun(['table'],
544 " $defPropName(Object.prototype, key, table[key]);\n" 520 js.block1(
545 " }\n" 521 new js.ForIn(
546 "})({\n"); 522 new js.VariableDeclarationList(
547 bool first = true; 523 [new js.VariableInitialization(
548 objectProperties.forEach((String name, String function) { 524 new js.VariableDeclaration('key'),
549 if (!first) targetBuffer.add(",\n"); 525 null)]),
550 targetBuffer.add("$_$name:$_$function"); 526 js.use('table'),
551 first = false; 527 new js.ExpressionStatement(
552 }); 528 js.call(
553 targetBuffer.add("\n})$N$n"); 529 js.use(defPropName),
530 [js.use('Object').dot('prototype'),
531 js.use('key'),
532 new js.PropertyAccess(js.use('table'),
533 js.use('key'))]))))),
534 [new js.ObjectInitializer(objectProperties)]);
535
536 if (emitter.compiler.enableMinification) targetBuffer.add(';');
537 targetBuffer.add(js.prettyPrint(
538 new js.ExpressionStatement(init), compiler));
539 targetBuffer.add('\n');
554 } 540 }
541
555 targetBuffer.add(nativeBuffer); 542 targetBuffer.add(nativeBuffer);
556 targetBuffer.add('\n'); 543 targetBuffer.add('\n');
557 } 544 }
558 } 545 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698