| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 class ConstantEmitter implements ConstantVisitor { | |
| 6 final Compiler compiler; | |
| 7 final Namer namer; | |
| 8 | |
| 9 CodeBuffer buffer; | |
| 10 bool shouldEmitCanonicalVersion; | |
| 11 | |
| 12 ConstantEmitter(this.compiler, this.namer); | |
| 13 | |
| 14 /** | |
| 15 * Unless the constant can be emitted multiple times (as for numbers and | |
| 16 * strings) use the canonical name. | |
| 17 */ | |
| 18 void emitCanonicalVersionOfConstant(Constant constant, CodeBuffer newBuffer) { | |
| 19 shouldEmitCanonicalVersion = true; | |
| 20 buffer = newBuffer; | |
| 21 _visit(constant); | |
| 22 } | |
| 23 | |
| 24 /** | |
| 25 * Emit the JavaScript code of the constant. If the constant must be | |
| 26 * canonicalized this method emits the initialization value. | |
| 27 */ | |
| 28 void emitJavaScriptCodeForConstant(Constant constant, CodeBuffer newBuffer) { | |
| 29 shouldEmitCanonicalVersion = false; | |
| 30 buffer = newBuffer; | |
| 31 _visit(constant); | |
| 32 } | |
| 33 | |
| 34 _visit(Constant constant) { | |
| 35 constant.accept(this); | |
| 36 } | |
| 37 | |
| 38 void visitSentinel(SentinelConstant constant) { | |
| 39 if (shouldEmitCanonicalVersion) { | |
| 40 buffer.add(namer.CURRENT_ISOLATE); | |
| 41 } else { | |
| 42 compiler.internalError( | |
| 43 "The parameter sentinel constant does not need specific JS code"); | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 void visitFunction(FunctionConstant constant) { | |
| 48 if (shouldEmitCanonicalVersion) { | |
| 49 buffer.add(namer.isolatePropertiesAccess(constant.element)); | |
| 50 } else { | |
| 51 compiler.internalError( | |
| 52 "The function constant does not need specific JS code"); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 void visitNull(NullConstant constant) { | |
| 57 buffer.add("null"); | |
| 58 } | |
| 59 | |
| 60 void visitInt(IntConstant constant) { | |
| 61 buffer.add(constant.value.toString()); | |
| 62 } | |
| 63 | |
| 64 void visitDouble(DoubleConstant constant) { | |
| 65 double value = constant.value; | |
| 66 if (value.isNaN()) { | |
| 67 buffer.add("(0/0)"); | |
| 68 } else if (value == double.INFINITY) { | |
| 69 buffer.add("(1/0)"); | |
| 70 } else if (value == -double.INFINITY) { | |
| 71 buffer.add("(-1/0)"); | |
| 72 } else { | |
| 73 buffer.add("$value"); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 void visitTrue(TrueConstant constant) { | |
| 78 buffer.add("true"); | |
| 79 } | |
| 80 | |
| 81 void visitFalse(FalseConstant constant) { | |
| 82 buffer.add("false"); | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * Write the contents of the quoted string to a [CodeBuffer] in | |
| 87 * a form that is valid as JavaScript string literal content. | |
| 88 * The string is assumed quoted by single quote characters. | |
| 89 */ | |
| 90 void writeEscapedString(DartString string, | |
| 91 CodeBuffer buffer, | |
| 92 Node diagnosticNode) { | |
| 93 Iterator<int> iterator = string.iterator(); | |
| 94 while (iterator.hasNext()) { | |
| 95 int code = iterator.next(); | |
| 96 if (code === $SQ) { | |
| 97 buffer.add(@"\'"); | |
| 98 } else if (code === $LF) { | |
| 99 buffer.add(@'\n'); | |
| 100 } else if (code === $CR) { | |
| 101 buffer.add(@'\r'); | |
| 102 } else if (code === $LS) { | |
| 103 // This Unicode line terminator and $PS are invalid in JS string | |
| 104 // literals. | |
| 105 buffer.add(@'\u2028'); | |
| 106 } else if (code === $PS) { | |
| 107 buffer.add(@'\u2029'); | |
| 108 } else if (code === $BACKSLASH) { | |
| 109 buffer.add(@'\\'); | |
| 110 } else { | |
| 111 if (code > 0xffff) { | |
| 112 compiler.reportError( | |
| 113 diagnosticNode, | |
| 114 'Unhandled non-BMP character: U+${code.toRadixString(16)}'); | |
| 115 } | |
| 116 // TODO(lrn): Consider whether all codes above 0x7f really need to | |
| 117 // be escaped. We build a Dart string here, so it should be a literal | |
| 118 // stage that converts it to, e.g., UTF-8 for a JS interpreter. | |
| 119 if (code < 0x20) { | |
| 120 buffer.add(@'\x'); | |
| 121 if (code < 0x10) buffer.add('0'); | |
| 122 buffer.add(code.toRadixString(16)); | |
| 123 } else if (code >= 0x80) { | |
| 124 if (code < 0x100) { | |
| 125 buffer.add(@'\x'); | |
| 126 } else { | |
| 127 buffer.add(@'\u'); | |
| 128 if (code < 0x1000) { | |
| 129 buffer.add('0'); | |
| 130 } | |
| 131 } | |
| 132 buffer.add(code.toRadixString(16)); | |
| 133 } else { | |
| 134 buffer.addCharCode(code); | |
| 135 } | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 void visitString(StringConstant constant) { | |
| 141 buffer.add("'"); | |
| 142 writeEscapedString(constant.value, buffer, constant.node); | |
| 143 buffer.add("'"); | |
| 144 } | |
| 145 | |
| 146 void emitCanonicalVersion(Constant constant) { | |
| 147 String name = namer.constantName(constant); | |
| 148 buffer.add(namer.isolatePropertiesAccessForConstant(name)); | |
| 149 } | |
| 150 | |
| 151 void visitList(ListConstant constant) { | |
| 152 if (shouldEmitCanonicalVersion) { | |
| 153 emitCanonicalVersion(constant); | |
| 154 } else { | |
| 155 shouldEmitCanonicalVersion = true; | |
| 156 buffer.add("${namer.ISOLATE}.makeConstantList"); | |
| 157 buffer.add("(["); | |
| 158 for (int i = 0; i < constant.entries.length; i++) { | |
| 159 if (i != 0) buffer.add(", "); | |
| 160 _visit(constant.entries[i]); | |
| 161 } | |
| 162 buffer.add("])"); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 String getJsConstructor(ClassElement element) { | |
| 167 return namer.isolatePropertiesAccess(element); | |
| 168 } | |
| 169 | |
| 170 void visitMap(MapConstant constant) { | |
| 171 if (shouldEmitCanonicalVersion) { | |
| 172 emitCanonicalVersion(constant); | |
| 173 } else { | |
| 174 void writeJsMap() { | |
| 175 buffer.add("{"); | |
| 176 int valueIndex = 0; | |
| 177 for (int i = 0; i < constant.keys.entries.length; i++) { | |
| 178 StringConstant key = constant.keys.entries[i]; | |
| 179 if (key.value == MapConstant.PROTO_PROPERTY) continue; | |
| 180 | |
| 181 if (valueIndex != 0) buffer.add(", "); | |
| 182 | |
| 183 // Keys in literal maps must be emitted in place. | |
| 184 emitJavaScriptCodeForConstant(key, buffer); | |
| 185 | |
| 186 buffer.add(": "); | |
| 187 emitCanonicalVersionOfConstant(constant.values[valueIndex++], buffer); | |
| 188 } | |
| 189 buffer.add("}"); | |
| 190 if (valueIndex != constant.values.length) { | |
| 191 compiler.internalError("Bad value count."); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 void badFieldCountError() { | |
| 196 compiler.internalError( | |
| 197 "Compiler and ConstantMap disagree on number of fields."); | |
| 198 } | |
| 199 | |
| 200 shouldEmitCanonicalVersion = true; | |
| 201 | |
| 202 ClassElement classElement = constant.type.element; | |
| 203 buffer.add("new "); | |
| 204 buffer.add(getJsConstructor(classElement)); | |
| 205 buffer.add("("); | |
| 206 // The arguments of the JavaScript constructor for any given Dart class | |
| 207 // are in the same order as the members of the class element. | |
| 208 int emittedArgumentCount = 0; | |
| 209 classElement.forEachInstanceField( | |
| 210 includeBackendMembers: true, | |
| 211 includeSuperMembers: true, | |
| 212 f: (ClassElement enclosing, Element field) { | |
| 213 if (emittedArgumentCount != 0) buffer.add(", "); | |
| 214 if (field.name == MapConstant.LENGTH_NAME) { | |
| 215 buffer.add(constant.keys.entries.length); | |
| 216 } else if (field.name == MapConstant.JS_OBJECT_NAME) { | |
| 217 writeJsMap(); | |
| 218 } else if (field.name == MapConstant.KEYS_NAME) { | |
| 219 emitCanonicalVersionOfConstant(constant.keys, buffer); | |
| 220 } else if (field.name == MapConstant.PROTO_VALUE) { | |
| 221 assert(constant.protoValue !== null); | |
| 222 emitCanonicalVersionOfConstant(constant.protoValue, buffer); | |
| 223 } else { | |
| 224 badFieldCountError(); | |
| 225 } | |
| 226 emittedArgumentCount++; | |
| 227 }); | |
| 228 if ((constant.protoValue === null && emittedArgumentCount != 3) || | |
| 229 (constant.protoValue !== null && emittedArgumentCount != 4)) { | |
| 230 badFieldCountError(); | |
| 231 } | |
| 232 buffer.add(")"); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 void visitConstructed(ConstructedConstant constant) { | |
| 237 if (shouldEmitCanonicalVersion) { | |
| 238 emitCanonicalVersion(constant); | |
| 239 } else { | |
| 240 shouldEmitCanonicalVersion = true; | |
| 241 | |
| 242 buffer.add("new "); | |
| 243 buffer.add(getJsConstructor(constant.type.element)); | |
| 244 buffer.add("("); | |
| 245 for (int i = 0; i < constant.fields.length; i++) { | |
| 246 if (i != 0) buffer.add(", "); | |
| 247 _visit(constant.fields[i]); | |
| 248 } | |
| 249 buffer.add(")"); | |
| 250 } | |
| 251 } | |
| 252 } | |
| OLD | NEW |