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

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

Issue 23045004: Throw when reflecting on elements not covered by a `MirrorsUsed` annotation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address Nicolas' comments. Created 7 years, 3 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 /** 7 /**
8 * A function element that represents a closure call. The signature is copied 8 * A function element that represents a closure call. The signature is copied
9 * from the given element. 9 * from the given element.
10 */ 10 */
(...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 // characters. 280 // characters.
281 static const FIELD_CODE_CHARACTERS = r"<=>?@{|}~%&'()*"; 281 static const FIELD_CODE_CHARACTERS = r"<=>?@{|}~%&'()*";
282 static const NO_FIELD_CODE = 0; 282 static const NO_FIELD_CODE = 0;
283 static const FIRST_FIELD_CODE = 1; 283 static const FIRST_FIELD_CODE = 1;
284 static const RANGE1_FIRST = 0x3c; // <=>?@ encodes 1..5 284 static const RANGE1_FIRST = 0x3c; // <=>?@ encodes 1..5
285 static const RANGE1_LAST = 0x40; 285 static const RANGE1_LAST = 0x40;
286 static const RANGE2_FIRST = 0x7b; // {|}~ encodes 6..9 286 static const RANGE2_FIRST = 0x7b; // {|}~ encodes 6..9
287 static const RANGE2_LAST = 0x7e; 287 static const RANGE2_LAST = 0x7e;
288 static const RANGE3_FIRST = 0x25; // %&'()*+ encodes 10..16 288 static const RANGE3_FIRST = 0x25; // %&'()*+ encodes 10..16
289 static const RANGE3_LAST = 0x2b; 289 static const RANGE3_LAST = 0x2b;
290 static const REFLECTION_MARKER = 0x2d;
290 291
291 jsAst.FunctionDeclaration get generateAccessorFunction { 292 jsAst.FunctionDeclaration get generateAccessorFunction {
292 const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1; 293 const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1;
293 const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1; 294 const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1;
294 const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST); 295 const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST);
295 const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST); 296 const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST);
296 const RANGE3_ADJUST = 297 const RANGE3_ADJUST =
297 - (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST); 298 - (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST);
298 299
299 String receiverParamName = compiler.enableMinification ? "r" : "receiver"; 300 String receiverParamName = compiler.enableMinification ? "r" : "receiver";
300 String valueParamName = compiler.enableMinification ? "v" : "value"; 301 String valueParamName = compiler.enableMinification ? "v" : "value";
302 String reflectableField = namer.reflectableField;
301 303
302 // function generateAccessor(field, prototype) { 304 // function generateAccessor(field, prototype) {
303 jsAst.Fun fun = js.fun(['field', 'prototype'], [ 305 jsAst.Fun fun = js.fun(['field', 'prototype'], [
304 js('var len = field.length'), 306 js('var len = field.length'),
305 js('var code = field.charCodeAt(len - 1)'), 307 js('var code = field.charCodeAt(len - 1)'),
308 js('var reflectable = false'),
309 js.if_('code == $REFLECTION_MARKER', [
310 js('len--'),
311 js('code = field.charCodeAt(len - 1)'),
312 js('field = field.substring(0, len)'),
313 js('reflectable = true')
314 ]),
306 js('code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST))' 315 js('code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST))'
307 ' ? code - $RANGE1_ADJUST' 316 ' ? code - $RANGE1_ADJUST'
308 ' : ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST))' 317 ' : ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST))'
309 ' ? code - $RANGE2_ADJUST' 318 ' ? code - $RANGE2_ADJUST'
310 ' : ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST))' 319 ' : ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST))'
311 ' ? code - $RANGE3_ADJUST' 320 ' ? code - $RANGE3_ADJUST'
312 ' : $NO_FIELD_CODE'), 321 ' : $NO_FIELD_CODE'),
313 322
314 // if (needsAccessor) { 323 // if (needsAccessor) {
315 js.if_('code', [ 324 js.if_('code', [
316 js('var getterCode = code & 3'), 325 js('var getterCode = code & 3'),
317 js('var setterCode = code >> 2'), 326 js('var setterCode = code >> 2'),
318 js('var accessorName = field = field.substring(0, len - 1)'), 327 js('var accessorName = field = field.substring(0, len - 1)'),
319 328
320 js('var divider = field.indexOf(":")'), 329 js('var divider = field.indexOf(":")'),
321 js.if_('divider > 0', [ // Colon never in first position. 330 js.if_('divider > 0', [ // Colon never in first position.
322 js('accessorName = field.substring(0, divider)'), 331 js('accessorName = field.substring(0, divider)'),
323 js('field = field.substring(divider + 1)') 332 js('field = field.substring(divider + 1)')
324 ]), 333 ]),
325 334
326 // if (needsGetter) { 335 // if (needsGetter) {
327 js.if_('getterCode', [ 336 js.if_('getterCode', [
328 js('var args = (getterCode & 2) ? "$receiverParamName" : ""'), 337 js('var args = (getterCode & 2) ? "$receiverParamName" : ""'),
329 js('var receiver = (getterCode & 1) ? "this" : "$receiverParamName"'), 338 js('var receiver = (getterCode & 1) ? "this" : "$receiverParamName"'),
330 js('var body = "return " + receiver + "." + field'), 339 js('var body = "return " + receiver + "." + field'),
331 js('prototype["${namer.getterPrefix}" + accessorName] = ' 340 js('prototype["${namer.getterPrefix}" + accessorName] = '
332 'new Function(args, body)') 341 'new Function(args, body)'),
342 js.if_('!reflectable', [
343 js('prototype["${namer.getterPrefix}" + accessorName].'
344 '$reflectableField = false')])
333 ]), 345 ]),
334 346
335 // if (needsSetter) { 347 // if (needsSetter) {
336 js.if_('setterCode', [ 348 js.if_('setterCode', [
337 js('var args = (setterCode & 2)' 349 js('var args = (setterCode & 2)'
338 ' ? "$receiverParamName,${_}$valueParamName"' 350 ' ? "$receiverParamName,${_}$valueParamName"'
339 ' : "$valueParamName"'), 351 ' : "$valueParamName"'),
340 js('var receiver = (setterCode & 1) ? "this" : "$receiverParamName"'), 352 js('var receiver = (setterCode & 1) ? "this" : "$receiverParamName"'),
341 js('var body = receiver + "." + field + "$_=$_$valueParamName"'), 353 js('var body = receiver + "." + field + "$_=$_$valueParamName"'),
342 js('prototype["${namer.setterPrefix}" + accessorName] = ' 354 js('prototype["${namer.setterPrefix}" + accessorName] = '
343 'new Function(args, body)') 355 'new Function(args, body)'),
356 js.if_('!reflectable', [
357 js('prototype["${namer.setterPrefix}" + accessorName].'
358 '$reflectableField = false')])
344 ]), 359 ]),
345 360
346 ]), 361 ]),
347 362
348 // return field; 363 // return field;
349 js.return_('field') 364 js.return_('field')
350 ]); 365 ]);
351 366
352 return new jsAst.FunctionDeclaration( 367 return new jsAst.FunctionDeclaration(
353 new jsAst.VariableDeclaration('generateAccessor'), 368 new jsAst.VariableDeclaration('generateAccessor'),
(...skipping 738 matching lines...) Expand 10 before | Expand all | Expand 10 after
1092 } else { 1107 } else {
1093 body = [js.return_(js('this')[namer.getName(member)](argumentsBuffer))]; 1108 body = [js.return_(js('this')[namer.getName(member)](argumentsBuffer))];
1094 } 1109 }
1095 1110
1096 jsAst.Fun function = js.fun(parametersBuffer, body); 1111 jsAst.Fun function = js.fun(parametersBuffer, body);
1097 1112
1098 defineStub(invocationName, function); 1113 defineStub(invocationName, function);
1099 1114
1100 String reflectionName = getReflectionName(selector, invocationName); 1115 String reflectionName = getReflectionName(selector, invocationName);
1101 if (reflectionName != null) { 1116 if (reflectionName != null) {
1102 defineStub('+$reflectionName', js('0')); 1117 var reflectable =
1118 js(backend.isAccessibleByReflection(member) ? '1' : '0');
1119 defineStub('+$reflectionName', reflectable);
1103 } 1120 }
1104 } 1121 }
1105 1122
1106 void addParameterStubs(FunctionElement member, 1123 void addParameterStubs(FunctionElement member,
1107 DefineStubFunction defineStub) { 1124 DefineStubFunction defineStub) {
1108 // We fill the lists depending on the selector. For example, 1125 // We fill the lists depending on the selector. For example,
1109 // take method foo: 1126 // take method foo:
1110 // foo(a, b, {c, d}); 1127 // foo(a, b, {c, d});
1111 // 1128 //
1112 // We may have multiple ways of calling foo: 1129 // We may have multiple ways of calling foo:
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
1263 jsAst.Expression code = backend.generatedCode[member]; 1280 jsAst.Expression code = backend.generatedCode[member];
1264 if (code == null) return; 1281 if (code == null) return;
1265 String name = namer.getName(member); 1282 String name = namer.getName(member);
1266 if (backend.isInterceptedMethod(member)) { 1283 if (backend.isInterceptedMethod(member)) {
1267 interceptorInvocationNames.add(name); 1284 interceptorInvocationNames.add(name);
1268 } 1285 }
1269 code = extendWithMetadata(member, code); 1286 code = extendWithMetadata(member, code);
1270 builder.addProperty(name, code); 1287 builder.addProperty(name, code);
1271 String reflectionName = getReflectionName(member, name); 1288 String reflectionName = getReflectionName(member, name);
1272 if (reflectionName != null) { 1289 if (reflectionName != null) {
1273 builder.addProperty('+$reflectionName', js('0')); 1290 var reflectable =
1291 js(backend.isAccessibleByReflection(member) ? '1' : '0');
1292 builder.addProperty('+$reflectionName', reflectable);
1274 } 1293 }
1275 code = backend.generatedBailoutCode[member]; 1294 code = backend.generatedBailoutCode[member];
1276 if (code != null) { 1295 if (code != null) {
1277 builder.addProperty(namer.getBailoutName(member), code); 1296 builder.addProperty(namer.getBailoutName(member), code);
1278 } 1297 }
1279 FunctionElement function = member; 1298 FunctionElement function = member;
1280 FunctionSignature parameters = function.computeSignature(compiler); 1299 FunctionSignature parameters = function.computeSignature(compiler);
1281 if (!parameters.optionalParameters.isEmpty) { 1300 if (!parameters.optionalParameters.isEmpty) {
1282 addParameterStubs(member, builder.addProperty); 1301 addParameterStubs(member, builder.addProperty);
1283 } 1302 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1318 1337
1319 String getReflectionNameInternal(elementOrSelector, String mangledName) { 1338 String getReflectionNameInternal(elementOrSelector, String mangledName) {
1320 String name = elementOrSelector.name.slowToString(); 1339 String name = elementOrSelector.name.slowToString();
1321 if (elementOrSelector.isGetter()) return name; 1340 if (elementOrSelector.isGetter()) return name;
1322 if (elementOrSelector.isSetter()) { 1341 if (elementOrSelector.isSetter()) {
1323 if (!mangledName.startsWith(namer.setterPrefix)) return '$name='; 1342 if (!mangledName.startsWith(namer.setterPrefix)) return '$name=';
1324 String base = mangledName.substring(namer.setterPrefix.length); 1343 String base = mangledName.substring(namer.setterPrefix.length);
1325 String getter = '${namer.getterPrefix}$base'; 1344 String getter = '${namer.getterPrefix}$base';
1326 mangledFieldNames[getter] = name; 1345 mangledFieldNames[getter] = name;
1327 recordedMangledNames.add(getter); 1346 recordedMangledNames.add(getter);
1328 return null; 1347 // TODO(karlklose,ahe): we do not actually need to store information
1348 // about the name of this setter in the output, but it is needed for
1349 // marking the function as invokable by reflection.
1350 return '$name=';
1329 } 1351 }
1330 if (elementOrSelector is Selector 1352 if (elementOrSelector is Selector
1331 || elementOrSelector.isFunction() 1353 || elementOrSelector.isFunction()
1332 || elementOrSelector.isConstructor()) { 1354 || elementOrSelector.isConstructor()) {
1333 int requiredParameterCount; 1355 int requiredParameterCount;
1334 int optionalParameterCount; 1356 int optionalParameterCount;
1335 String namedArguments = ''; 1357 String namedArguments = '';
1336 bool isConstructor = false; 1358 bool isConstructor = false;
1337 if (elementOrSelector is Selector) { 1359 if (elementOrSelector is Selector) {
1338 Selector selector = elementOrSelector; 1360 Selector selector = elementOrSelector;
(...skipping 515 matching lines...) Expand 10 before | Expand all | Expand 10 after
1854 } 1876 }
1855 } 1877 }
1856 int code = getterCode + (setterCode << 2); 1878 int code = getterCode + (setterCode << 2);
1857 if (code == 0) { 1879 if (code == 0) {
1858 compiler.reportInternalError( 1880 compiler.reportInternalError(
1859 field, 'Internal error: code is 0 ($element/$field)'); 1881 field, 'Internal error: code is 0 ($element/$field)');
1860 } else { 1882 } else {
1861 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]); 1883 buffer.write(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]);
1862 } 1884 }
1863 } 1885 }
1886 if (backend.isAccessibleByReflection(field)) {
1887 buffer.write(new String.fromCharCode(REFLECTION_MARKER));
1888 }
1864 } 1889 }
1865 }); 1890 });
1866 } 1891 }
1867 1892
1868 bool fieldsAdded = buffer.length > bufferClassLength; 1893 bool fieldsAdded = buffer.length > bufferClassLength;
1869 String compactClassData = buffer.toString(); 1894 String compactClassData = buffer.toString();
1870 jsAst.Expression classDataNode = js.string(compactClassData); 1895 jsAst.Expression classDataNode = js.string(compactClassData);
1871 if (hasMetadata) { 1896 if (hasMetadata) {
1872 fieldMetadata.insert(0, classDataNode); 1897 fieldMetadata.insert(0, classDataNode);
1873 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata); 1898 classDataNode = new jsAst.ArrayInitializer.from(fieldMetadata);
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after
2293 .toSet(); 2318 .toSet();
2294 2319
2295 for (Element element in Elements.sortedByPosition(elements)) { 2320 for (Element element in Elements.sortedByPosition(elements)) {
2296 CodeBuffer buffer = bufferForElement(element, eagerBuffer); 2321 CodeBuffer buffer = bufferForElement(element, eagerBuffer);
2297 jsAst.Expression code = backend.generatedCode[element]; 2322 jsAst.Expression code = backend.generatedCode[element];
2298 String name = namer.getName(element); 2323 String name = namer.getName(element);
2299 code = extendWithMetadata(element, code); 2324 code = extendWithMetadata(element, code);
2300 emitStaticFunction(buffer, name, code); 2325 emitStaticFunction(buffer, name, code);
2301 String reflectionName = getReflectionName(element, name); 2326 String reflectionName = getReflectionName(element, name);
2302 if (reflectionName != null) { 2327 if (reflectionName != null) {
2303 buffer.write(',$n$n"+$reflectionName":${_}0'); 2328 var reflectable = backend.isAccessibleByReflection(element) ? 1 : 0;
2329 buffer.write(',$n$n"+$reflectionName":${_}$reflectable');
2304 } 2330 }
2305 jsAst.Expression bailoutCode = backend.generatedBailoutCode[element]; 2331 jsAst.Expression bailoutCode = backend.generatedBailoutCode[element];
2306 if (bailoutCode != null) { 2332 if (bailoutCode != null) {
2307 pendingElementsWithBailouts.remove(element); 2333 pendingElementsWithBailouts.remove(element);
2308 emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode); 2334 emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode);
2309 } 2335 }
2310 } 2336 }
2311 2337
2312 if (!pendingElementsWithBailouts.isEmpty) { 2338 if (!pendingElementsWithBailouts.isEmpty) {
2313 addComment('pendingElementsWithBailouts', eagerBuffer); 2339 addComment('pendingElementsWithBailouts', eagerBuffer);
(...skipping 1709 matching lines...) Expand 10 before | Expand all | Expand 10 after
4023 // TODO(ahe): Remove this when deferred loading is fully implemented. 4049 // TODO(ahe): Remove this when deferred loading is fully implemented.
4024 void warnNotImplemented(Element element, String message) { 4050 void warnNotImplemented(Element element, String message) {
4025 compiler.reportMessage(compiler.spanFromSpannable(element), 4051 compiler.reportMessage(compiler.spanFromSpannable(element),
4026 MessageKind.GENERIC.error({'text': message}), 4052 MessageKind.GENERIC.error({'text': message}),
4027 api.Diagnostic.WARNING); 4053 api.Diagnostic.WARNING);
4028 } 4054 }
4029 4055
4030 // TODO(ahe): This code should be integrated in finishClasses. 4056 // TODO(ahe): This code should be integrated in finishClasses.
4031 String getReflectionDataParser() { 4057 String getReflectionDataParser() {
4032 String metadataField = '"${namer.metadataField}"'; 4058 String metadataField = '"${namer.metadataField}"';
4059 String reflectableField = namer.reflectableField;
4033 return ''' 4060 return '''
4034 (function (reflectionData) { 4061 (function (reflectionData) {
4035 ''' 4062 '''
4036 // [map] returns an object literal that V8 shouldn't try to optimize with a 4063 // [map] returns an object literal that V8 shouldn't try to optimize with a
4037 // hidden class. This prevents a potential performance problem where V8 tries 4064 // hidden class. This prevents a potential performance problem where V8 tries
4038 // to build a hidden class for an object used as a hashMap. 4065 // to build a hidden class for an object used as a hashMap.
4039 ''' 4066 '''
4040 function map(x){x={x:x};delete x.x;return x} 4067 function map(x){x={x:x};delete x.x;return x}
4041 if (!init.libraries) init.libraries = []; 4068 if (!init.libraries) init.libraries = [];
4042 if (!init.mangledNames) init.mangledNames = map(); 4069 if (!init.mangledNames) init.mangledNames = map();
(...skipping 28 matching lines...) Expand all
4071 var functions = []; 4098 var functions = [];
4072 function processStatics(descriptor) { 4099 function processStatics(descriptor) {
4073 for (var property in descriptor) { 4100 for (var property in descriptor) {
4074 if (!hasOwnProperty.call(descriptor, property)) continue; 4101 if (!hasOwnProperty.call(descriptor, property)) continue;
4075 if (property === "") continue; 4102 if (property === "") continue;
4076 var element = descriptor[property]; 4103 var element = descriptor[property];
4077 var firstChar = property.substring(0, 1); 4104 var firstChar = property.substring(0, 1);
4078 var previousProperty; 4105 var previousProperty;
4079 if (firstChar === "+") { 4106 if (firstChar === "+") {
4080 mangledGlobalNames[previousProperty] = property.substring(1); 4107 mangledGlobalNames[previousProperty] = property.substring(1);
4081 if (element && element.length) ''' // Breaking long line. 4108 descriptor[previousProperty].''' // Break long line.
4109 '''$reflectableField = (descriptor[property] == 1);
4110 if (element && element.length) ''' // Break long line.
4082 '''init.interfaces[previousProperty] = element; 4111 '''init.interfaces[previousProperty] = element;
4083 } else if (firstChar === "@") { 4112 } else if (firstChar === "@") {
4084 property = property.substring(1); 4113 property = property.substring(1);
4085 ${namer.CURRENT_ISOLATE}[property][$metadataField] = element; 4114 ${namer.CURRENT_ISOLATE}[property][$metadataField] = element;
4086 } else if (typeof element === "function") { 4115 } else if (typeof element === "function") {
4087 ${namer.CURRENT_ISOLATE}[previousProperty = property] = element; 4116 ${namer.CURRENT_ISOLATE}[previousProperty = property] = element;
4088 functions.push(property); 4117 functions.push(property);
4089 } else { 4118 } else {
4090 previousProperty = property; 4119 previousProperty = property;
4091 var newDesc = {}; 4120 var newDesc = {};
4092 var previousProp; 4121 var previousProp;
4093 for (var prop in element) { 4122 for (var prop in element) {
4094 if (!hasOwnProperty.call(element, prop)) continue; 4123 if (!hasOwnProperty.call(element, prop)) continue;
4095 firstChar = prop.substring(0, 1); 4124 firstChar = prop.substring(0, 1);
4096 if (prop === "static") { 4125 if (prop === "static") {
4097 processStatics(init.statics[property] = element[prop]); 4126 processStatics(init.statics[property] = element[prop]);
4098 } else if (firstChar === "+") { 4127 } else if (firstChar === "+") {
4099 mangledNames[previousProp] = prop.substring(1); 4128 mangledNames[previousProp] = prop.substring(1);
4129 element[previousProp].''' // Break long line.
4130 '''$reflectableField = (element[prop] == 1);
4100 } else if (firstChar === "@" && prop !== "@") { 4131 } else if (firstChar === "@" && prop !== "@") {
4101 newDesc[prop.substring(1)][$metadataField] = element[prop]; 4132 newDesc[prop.substring(1)][$metadataField] = element[prop];
4102 } else { 4133 } else {
4103 newDesc[previousProp = prop] = element[prop]; 4134 newDesc[previousProp = prop] = element[prop];
4104 } 4135 }
4105 } 4136 }
4106 $classesCollector[property] = newDesc; 4137 $classesCollector[property] = newDesc;
4107 classes.push(property); 4138 classes.push(property);
4108 } 4139 }
4109 } 4140 }
(...skipping 11 matching lines...) Expand all
4121 4152
4122 const String HOOKS_API_USAGE = """ 4153 const String HOOKS_API_USAGE = """
4123 // The code supports the following hooks: 4154 // The code supports the following hooks:
4124 // dartPrint(message) - if this function is defined it is called 4155 // dartPrint(message) - if this function is defined it is called
4125 // instead of the Dart [print] method. 4156 // instead of the Dart [print] method.
4126 // dartMainRunner(main) - if this function is defined, the Dart [main] 4157 // dartMainRunner(main) - if this function is defined, the Dart [main]
4127 // method will not be invoked directly. 4158 // method will not be invoked directly.
4128 // Instead, a closure that will invoke [main] is 4159 // Instead, a closure that will invoke [main] is
4129 // passed to [dartMainRunner]. 4160 // passed to [dartMainRunner].
4130 """; 4161 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698