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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart

Issue 1198293002: dart2js: Use an abstract Name class for names in the generated JavaScript ast. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Comments Created 5 years, 5 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
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 dart2js.js_emitter; 5 part of dart2js.js_emitter;
6 6
7 class NsmEmitter extends CodeEmitterHelper { 7 class NsmEmitter extends CodeEmitterHelper {
8 final List<Selector> trivialNsmHandlers = <Selector>[]; 8 final List<Selector> trivialNsmHandlers = <Selector>[];
9 9
10 /// If this is true then we can generate the noSuchMethod handlers at startup 10 /// If this is true then we can generate the noSuchMethod handlers at startup
11 /// time, instead of them being emitted as part of the Object class. 11 /// time, instead of them being emitted as part of the Object class.
12 bool get generateTrivialNsmHandlers => true; 12 bool get generateTrivialNsmHandlers => true;
13 13
14 // If we need fewer than this many noSuchMethod handlers we can save space by 14 // If we need fewer than this many noSuchMethod handlers we can save space by
15 // just emitting them in JS, rather than emitting the JS needed to generate 15 // just emitting them in JS, rather than emitting the JS needed to generate
16 // them at run time. 16 // them at run time.
17 static const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10; 17 static const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10;
18 18
19 static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4; 19 static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4;
20 20
21 void emitNoSuchMethodHandlers(AddPropertyFunction addProperty) { 21 void emitNoSuchMethodHandlers(AddPropertyFunction addProperty) {
22 22
23 ClassStubGenerator generator = 23 ClassStubGenerator generator =
24 new ClassStubGenerator(compiler, namer, backend); 24 new ClassStubGenerator(compiler, namer, backend);
25 25
26 // Keep track of the JavaScript names we've already added so we 26 // Keep track of the JavaScript names we've already added so we
27 // do not introduce duplicates (bad for code size). 27 // do not introduce duplicates (bad for code size).
28 Map<String, Selector> addedJsNames 28 Map<jsAst.Name, Selector> addedJsNames
29 = generator.computeSelectorsForNsmHandlers(); 29 = generator.computeSelectorsForNsmHandlers();
30 30
31 // Set flag used by generateMethod helper below. If we have very few 31 // Set flag used by generateMethod helper below. If we have very few
32 // handlers we use addProperty for them all, rather than try to generate 32 // handlers we use addProperty for them all, rather than try to generate
33 // them at runtime. 33 // them at runtime.
34 bool haveVeryFewNoSuchMemberHandlers = 34 bool haveVeryFewNoSuchMemberHandlers =
35 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); 35 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS);
36 for (String jsName in addedJsNames.keys.toList()..sort()) { 36 List<jsAst.Name> names = addedJsNames.keys.toList()
37 ..sort();
38 for (jsAst.Name jsName in names) {
37 Selector selector = addedJsNames[jsName]; 39 Selector selector = addedJsNames[jsName];
38 String reflectionName = emitter.getReflectionName(selector, jsName); 40 String reflectionName = emitter.getReflectionName(selector, jsName);
39 41
40 if (reflectionName != null) { 42 if (reflectionName != null) {
41 emitter.mangledFieldNames[jsName] = reflectionName; 43 emitter.mangledFieldNames[jsName] = reflectionName;
42 } 44 }
43 45
44 List<jsAst.Expression> argNames = 46 List<jsAst.Expression> argNames =
45 selector.callStructure.getOrderedNamedArguments().map((String name) => 47 selector.callStructure.getOrderedNamedArguments().map((String name) =>
46 js.string(name)).toList(); 48 js.string(name)).toList();
47 int type = selector.invocationMirrorKind; 49 int type = selector.invocationMirrorKind;
48 if (!haveVeryFewNoSuchMemberHandlers && 50 if (!haveVeryFewNoSuchMemberHandlers &&
49 isTrivialNsmHandler(type, argNames, selector, jsName) && 51 isTrivialNsmHandler(type, argNames, selector, jsName) &&
50 reflectionName == null) { 52 reflectionName == null) {
51 trivialNsmHandlers.add(selector); 53 trivialNsmHandlers.add(selector);
52 } else { 54 } else {
53 StubMethod method = 55 StubMethod method =
54 generator.generateStubForNoSuchMethod(jsName, selector); 56 generator.generateStubForNoSuchMethod(jsName, selector);
55 addProperty(method.name, method.code); 57 addProperty(method.name, method.code);
56 if (reflectionName != null) { 58 if (reflectionName != null) {
57 bool accessible = 59 bool accessible =
58 compiler.world.allFunctions.filter(selector, null).any( 60 compiler.world.allFunctions.filter(selector, null).any(
59 (Element e) => backend.isAccessibleByReflection(e)); 61 (Element e) => backend.isAccessibleByReflection(e));
60 addProperty('+$reflectionName', js(accessible ? '2' : '0')); 62 addProperty(namer.asName('+$reflectionName'),
63 js(accessible ? '2' : '0'));
61 } 64 }
62 } 65 }
63 } 66 }
64 } 67 }
65 68
66 // Identify the noSuchMethod handlers that are so simple that we can 69 // Identify the noSuchMethod handlers that are so simple that we can
67 // generate them programatically. 70 // generate them programatically.
68 bool isTrivialNsmHandler( 71 bool isTrivialNsmHandler(
69 int type, List argNames, Selector selector, String internalName) { 72 int type, List argNames, Selector selector, jsAst.Name internalName) {
70 if (!generateTrivialNsmHandlers) return false; 73 if (!generateTrivialNsmHandlers) return false;
71 // Check for interceptor calling convention.
72 if (backend.isInterceptedName(selector.name)) {
73 // We can handle the calling convention used by intercepted names in the
74 // diff encoding, but we don't use that for non-minified code.
75 if (!compiler.enableMinification) return false;
76 String shortName = namer.invocationMirrorInternalName(selector);
77 if (shortName.length > MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) {
78 return false;
79 }
80 }
81 // Check for named arguments. 74 // Check for named arguments.
82 if (argNames.length != 0) return false; 75 if (argNames.length != 0) return false;
83 // Check for unexpected name (this doesn't really happen). 76 // Check for unexpected name (this doesn't really happen).
84 if (internalName.startsWith(namer.getterPrefix[0])) return type == 1; 77 if (internalName is GetterName) return type == 1;
85 if (internalName.startsWith(namer.setterPrefix[0])) return type == 2; 78 if (internalName is SetterName) return type == 2;
86 return type == 0; 79 return type == 0;
87 } 80 }
88 81
89 /** 82 /**
90 * Adds (at runtime) the handlers to the Object class which catch calls to 83 * Adds (at runtime) the handlers to the Object class which catch calls to
91 * methods that the object does not have. The handlers create an invocation 84 * methods that the object does not have. The handlers create an invocation
92 * mirror object. 85 * mirror object.
93 * 86 *
94 * The current version only gives you the minified name when minifying (when 87 * The current version only gives you the minified name when minifying (when
95 * not minifying this method is not called). 88 * not minifying this method is not called).
(...skipping 25 matching lines...) Expand all
121 * The reason we don't encode long minified names with this method is that 114 * The reason we don't encode long minified names with this method is that
122 * decoding the base 88 numbers would overflow JavaScript's puny integers. 115 * decoding the base 88 numbers would overflow JavaScript's puny integers.
123 * 116 *
124 * There are some selectors that have a special calling convention (because 117 * There are some selectors that have a special calling convention (because
125 * they are called with the receiver as the first argument). They need a 118 * they are called with the receiver as the first argument). They need a
126 * slightly different noSuchMethod handler, so we handle these first. 119 * slightly different noSuchMethod handler, so we handle these first.
127 */ 120 */
128 List<jsAst.Statement> buildTrivialNsmHandlers() { 121 List<jsAst.Statement> buildTrivialNsmHandlers() {
129 List<jsAst.Statement> statements = <jsAst.Statement>[]; 122 List<jsAst.Statement> statements = <jsAst.Statement>[];
130 if (trivialNsmHandlers.length == 0) return statements; 123 if (trivialNsmHandlers.length == 0) return statements;
131 // Sort by calling convention, JS name length and by JS name.
132 trivialNsmHandlers.sort((a, b) {
133 bool aIsIntercepted = backend.isInterceptedName(a.name);
134 bool bIsIntercepted = backend.isInterceptedName(b.name);
135 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1;
136 String aName = namer.invocationMirrorInternalName(a);
137 String bName = namer.invocationMirrorInternalName(b);
138 if (aName.length != bName.length) return aName.length - bName.length;
139 return aName.compareTo(bName);
140 });
141 124
142 // Find out how many selectors there are with the special calling 125 // Find out how many selectors there are with the special calling
143 // convention. 126 // convention.
144 int firstNormalSelector = trivialNsmHandlers.length; 127 bool hasSpecialCallingConvention(Selector selector) {
145 for (int i = 0; i < trivialNsmHandlers.length; i++) { 128 return backend.isInterceptedName(selector.name);
146 if (!backend.isInterceptedName(trivialNsmHandlers[i].name)) {
147 firstNormalSelector = i;
148 break;
149 }
150 } 129 }
130 Iterable<Selector> specialSelectors = trivialNsmHandlers.where(
131 (Selector s) => backend.isInterceptedName(s.name));
132 Iterable<Selector> ordinarySelectors = trivialNsmHandlers.where(
133 (Selector s) => !backend.isInterceptedName(s.name));
151 134
152 // Get the short names (JS names, perhaps minified). 135 // Get the short names (JS names, perhaps minified).
153 Iterable<String> shorts = trivialNsmHandlers.map((selector) => 136 List<jsAst.Name> specialShorts =
154 namer.invocationMirrorInternalName(selector)); 137 specialSelectors.map(namer.invocationMirrorInternalName);
155 var diffEncoding = new StringBuffer(); 138 List<jsAst.Name> ordinaryShorts =
156 139 ordinarySelectors.map(namer.invocationMirrorInternalName);
157 // Treat string as a number in base 88 with digits in ASCII order from # to
158 // z. The short name sorting is based on length, and uses ASCII order for
159 // equal length strings so this means that names are ascending. The hash
160 // character, #, is never given as input, but we need it because it's the
161 // implicit leading zero (otherwise we could not code names with leading
162 // dollar signs).
163 int fromBase88(String x) {
164 int answer = 0;
165 for (int i = 0; i < x.length; i++) {
166 int c = x.codeUnitAt(i);
167 // No support for Unicode minified identifiers in JS.
168 assert(c >= $$ && c <= $z);
169 answer *= 88;
170 answer += c - $HASH;
171 }
172 return answer;
173 }
174
175 // Big endian encoding, A = 0, B = 1...
176 // A lower case letter terminates the number.
177 String toBase26(int x) {
178 int c = x;
179 var encodingChars = <int>[];
180 encodingChars.add($a + (c % 26));
181 while (true) {
182 c ~/= 26;
183 if (c == 0) break;
184 encodingChars.add($A + (c % 26));
185 }
186 return new String.fromCharCodes(encodingChars.reversed.toList());
187 }
188 140
189 bool minify = compiler.enableMinification; 141 bool minify = compiler.enableMinification;
190 bool useDiffEncoding = minify && shorts.length > 30; 142 bool useDiffEncoding = minify && trivialNsmHandlers.length > 30;
191 143
192 int previous = 0; 144 jsAst.Expression diffEncoding = new _DiffEncodedListOfNames(
193 int nameCounter = 0; 145 [specialShorts, ordinaryShorts],
194 for (String short in shorts) { 146 useDiffEncoding);
195 // Emit period that resets the diff base to zero when we switch to normal
196 // calling convention (this avoids the need to code negative diffs).
197 if (useDiffEncoding && nameCounter == firstNormalSelector) {
198 diffEncoding.write(".");
199 previous = 0;
200 }
201 if (short.length <= MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING &&
202 useDiffEncoding) {
203 int base63 = fromBase88(short);
204 int diff = base63 - previous;
205 previous = base63;
206 String base26Diff = toBase26(diff);
207 diffEncoding.write(base26Diff);
208 } else {
209 if (useDiffEncoding || diffEncoding.length != 0) {
210 diffEncoding.write(",");
211 }
212 diffEncoding.write(short);
213 }
214 nameCounter++;
215 }
216 147
217 // Startup code that loops over the method names and puts handlers on the 148 // Startup code that loops over the method names and puts handlers on the
218 // Object class to catch noSuchMethod invocations. 149 // Object class to catch noSuchMethod invocations.
219 ClassElement objectClass = compiler.objectClass; 150 ClassElement objectClass = compiler.objectClass;
220 jsAst.Expression createInvocationMirror = backend.emitter 151 jsAst.Expression createInvocationMirror = backend.emitter
221 .staticFunctionAccess(backend.getCreateInvocationMirror()); 152 .staticFunctionAccess(backend.getCreateInvocationMirror());
222 if (useDiffEncoding) { 153 if (useDiffEncoding) {
223 statements.add(js.statement('''{ 154 statements.add(js.statement('''{
224 var objectClassObject = processedClasses.collected[#objectClass], 155 var objectClassObject = processedClasses.collected[#objectClass],
225 shortNames = #diffEncoding.split(","), 156 nameSequences = #diffEncoding.split("."),
226 nameNumber = 0, 157 shortNames = [];
227 diffEncodedString = shortNames[0], 158 for (var j = 0; j < nameSequences.length; ++j) {
228 calculatedShortNames = [0, 1]; // 0, 1 are args for splice. 159 var sequence = nameSequences[j].split(","),
229 // If we are loading a deferred library the object class will not be i n 160 nameNumber = 0,
230 // the collectedClasses so objectClassObject is undefined, and we skip 161 diffEncodedString = sequence[0];
sra1 2015/06/23 20:54:16 Move under 'if (sequence.length)'. If sequence can
herhut 2015/06/24 07:55:07 Done.
231 // setting up the names. 162 // If we are loading a deferred library the object class will not be
232 163 // in the collectedClasses so objectClassObject is undefined, and we
233 if (objectClassObject) { 164 // skip setting up the names.
234 if (objectClassObject instanceof Array) 165 // Likewise, if the current sequence is empty, we don't process it.
235 objectClassObject = objectClassObject[1]; 166 if (objectClassObject && sequence.length) {
236 for (var i = 0; i < diffEncodedString.length; i++) { 167 if (objectClassObject instanceof Array)
237 var codes = [], 168 objectClassObject = objectClassObject[1];
sra1 2015/06/23 20:54:16 This should probably be hoisted out of the j loop.
herhut 2015/06/24 07:55:07 Done.
238 diff = 0, 169 for (var i = 0; i < diffEncodedString.length; i++) {
239 digit = diffEncodedString.charCodeAt(i); 170 var codes = [],
240 if (digit == ${$PERIOD}) { 171 diff = 0,
241 nameNumber = 0; 172 digit = diffEncodedString.charCodeAt(i);
242 digit = diffEncodedString.charCodeAt(++i); 173 for (; digit <= ${$Z};) {
174 diff *= 26;
175 diff += (digit - ${$A});
176 digit = diffEncodedString.charCodeAt(++i);
177 }
178 diff *= 26;
179 diff += (digit - ${$a});
180 nameNumber += diff;
181 for (var remaining = nameNumber;
182 remaining > 0;
183 remaining = (remaining / 88) | 0) {
184 codes.unshift(${$HASH} + remaining % 88);
sra1 2015/06/23 20:54:16 I'm curious of you noticed this showing up in star
herhut 2015/06/24 07:55:07 No, I was not even aware this existed before worki
185 }
186 shortNames.push(
187 String.fromCharCode.apply(String, codes));
243 } 188 }
244 for (; digit <= ${$Z};) { 189 Array.prototype.push.apply(shortNames, sequence.shift());
245 diff *= 26;
246 diff += (digit - ${$A});
247 digit = diffEncodedString.charCodeAt(++i);
248 }
249 diff *= 26;
250 diff += (digit - ${$a});
251 nameNumber += diff;
252 for (var remaining = nameNumber;
253 remaining > 0;
254 remaining = (remaining / 88) | 0) {
255 codes.unshift(${$HASH} + remaining % 88);
256 }
257 calculatedShortNames.push(
258 String.fromCharCode.apply(String, codes));
259 } 190 }
260 shortNames.splice.apply(shortNames, calculatedShortNames);
261 } 191 }
262 }''', {'objectClass': js.string(namer.className(objectClass)), 192 }''', {'objectClass': js.quoteName(namer.className(objectClass)),
263 'diffEncoding': js.string('$diffEncoding')})); 193 'diffEncoding': diffEncoding}));
264 } else { 194 } else {
265 // No useDiffEncoding version. 195 // No useDiffEncoding version.
266 Iterable<String> longs = trivialNsmHandlers.map((selector) => 196 Iterable<String> longs = trivialNsmHandlers.map((selector) =>
267 selector.invocationMirrorMemberName); 197 selector.invocationMirrorMemberName);
268 statements.add(js.statement( 198 statements.add(js.statement(
269 'var objectClassObject = processedClasses.collected[#objectClass],' 199 'var objectClassObject = processedClasses.collected[#objectClass],'
270 ' shortNames = #diffEncoding.split(",")', 200 ' shortNames = #diffEncoding.split(",")',
271 {'objectClass': js.string(namer.className(objectClass)), 201 {'objectClass': js.quoteName(namer.className(objectClass)),
272 'diffEncoding': js.string('$diffEncoding')})); 202 'diffEncoding': diffEncoding}));
273 if (!minify) { 203 if (!minify) {
274 statements.add(js.statement('var longNames = #longs.split(",")', 204 statements.add(js.statement('var longNames = #longs.split(",")',
275 {'longs': js.string(longs.join(','))})); 205 {'longs': js.string(longs.join(','))}));
276 } 206 }
277 statements.add(js.statement( 207 statements.add(js.statement(
278 'if (objectClassObject instanceof Array)' 208 'if (objectClassObject instanceof Array)'
279 ' objectClassObject = objectClassObject[1];')); 209 ' objectClassObject = objectClassObject[1];'));
280 } 210 }
281 211
282 List<jsAst.Expression> sliceOffsetArguments = 212 List<jsAst.Expression> sliceOffsetArguments =
283 firstNormalSelector == 0 213 specialSelectors.isEmpty
284 ? [] 214 ? []
285 : (firstNormalSelector == shorts.length 215 : (ordinarySelectors.isEmpty
286 ? [js.number(1)] 216 ? [js.number(1)]
287 : [js('(j < #) ? 1 : 0', js.number(firstNormalSelector))]); 217 : [js('(j < #) ? 1 : 0', js.number(specialSelectors.length))]);
sra1 2015/06/23 20:54:16 I fixed a bug in this code, sorry that you will ha
herhut 2015/06/24 07:55:07 Acknowledged.
288 218
289 var sliceOffsetParams = sliceOffsetArguments.isEmpty ? [] : ['sliceOffset']; 219 var sliceOffsetParams = sliceOffsetArguments.isEmpty ? [] : ['sliceOffset'];
290 220
291 statements.add(js.statement(''' 221 statements.add(js.statement('''
292 // If we are loading a deferred library the object class will not be in 222 // If we are loading a deferred library the object class will not be in
293 // the collectedClasses so objectClassObject is undefined, and we skip 223 // the collectedClasses so objectClassObject is undefined, and we skip
294 // setting up the names. 224 // setting up the names.
295 if (objectClassObject) { 225 if (objectClassObject) {
296 for (var j = 0; j < shortNames.length; j++) { 226 for (var j = 0; j < shortNames.length; j++) {
297 var type = 0; 227 var type = 0;
(...skipping 19 matching lines...) Expand all
317 }''', { 247 }''', {
318 'sliceOffsetParams': sliceOffsetParams, 248 'sliceOffsetParams': sliceOffsetParams,
319 'noSuchMethodName': namer.noSuchMethodName, 249 'noSuchMethodName': namer.noSuchMethodName,
320 'createInvocationMirror': createInvocationMirror, 250 'createInvocationMirror': createInvocationMirror,
321 'names': minify ? 'shortNames' : 'longNames', 251 'names': minify ? 'shortNames' : 'longNames',
322 'sliceOffsetArguments': sliceOffsetArguments})); 252 'sliceOffsetArguments': sliceOffsetArguments}));
323 253
324 return statements; 254 return statements;
325 } 255 }
326 } 256 }
257
258 /// When pretty printed, this node computes a diff-encoded string for the list
259 /// of given names.
260 ///
261 /// See [buildTrivialNsmHandlers].
262 class _DiffEncodedListOfNames extends jsAst.DeferredString
263 implements AstContainer {
264 String _cachedValue;
265 jsAst.ArrayInitializer ast;
266 bool useDiffEncoding;
267
268 _DiffEncodedListOfNames(Iterable<Iterable<jsAst.Name>> names,
269 this.useDiffEncoding) {
270 // Store the names in ArrayInitializer nodes to make them discoverable
271 // by traversals of the ast.
272 ast = new jsAst.ArrayInitializer(
273 names.map((Iterable i) => new jsAst.ArrayInitializer(i.toList()))
274 .toList());
275 }
276
277 void _computeDiffEncodingForList(Iterable<jsAst.Name> names,
278 StringBuffer diffEncoding) {
279 // Treat string as a number in base 88 with digits in ASCII order from # to
280 // z. The short name sorting is based on length, and uses ASCII order for
281 // equal length strings so this means that names are ascending. The hash
282 // character, #, is never given as input, but we need it because it's the
283 // implicit leading zero (otherwise we could not code names with leading
284 // dollar signs).
285 int fromBase88(String x) {
286 int answer = 0;
287 for (int i = 0; i < x.length; i++) {
288 int c = x.codeUnitAt(i);
289 // No support for Unicode minified identifiers in JS.
290 assert(c >= $$ && c <= $z);
291 answer *= 88;
292 answer += c - $HASH;
293 }
294 return answer;
295 }
296
297 // Big endian encoding, A = 0, B = 1...
298 // A lower case letter terminates the number.
299 String toBase26(int x) {
300 int c = x;
301 var encodingChars = <int>[];
302 encodingChars.add($a + (c % 26));
303 while (true) {
304 c ~/= 26;
305 if (c == 0) break;
306 encodingChars.add($A + (c % 26));
307 }
308 return new String.fromCharCodes(encodingChars.reversed.toList());
309 }
310
311 // Sort by length, then lexicographic.
312 int compare(String a, String b) {
313 if (a.length != b.length) return a.length - b.length;
314 return a.compareTo(b);
315 }
316
317 List<String> shorts =
318 names.map((jsAst.Name name) => name.name)
319 .toList()
320 ..sort(compare);
321
322 int previous = 0;
323 for (String short in shorts) {
324 if (useDiffEncoding &&
325 short.length <= NsmEmitter.MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) {
326 int base63 = fromBase88(short);
327 int diff = base63 - previous;
328 previous = base63;
329 String base26Diff = toBase26(diff);
330 diffEncoding.write(base26Diff);
331 } else {
332 if (useDiffEncoding || diffEncoding.length != 0) {
333 diffEncoding.write(',');
334 }
335 diffEncoding.write(short);
336 }
337 }
338 }
339
340 String _computeDiffEncoding() {
341 StringBuffer buffer = new StringBuffer();
342 for (jsAst.ArrayInitializer list in ast.elements) {
343 if (buffer.isNotEmpty) {
344 if (useDiffEncoding) {
345 // Emit period that resets the diff base to zero when we switch to
346 // normal calling convention (this avoids the need to code negative
347 // diffs).
348 buffer.write(".");
349 } else {
350 // Write a separator for the next sequence.
351 buffer.write(",");
352 }
353 }
354 List<jsAst.Name> names = list.elements;
355 _computeDiffEncodingForList(names, buffer);
356 }
357 return buffer.toString();
358 }
359
360 String get value {
361 if (_cachedValue == null) {
362 _cachedValue = _computeDiffEncoding();
363 }
364
365 return _cachedValue;
366 }
367 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698