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 part of js_backend; | |
6 | |
7 const JAVA_SCRIPT_CONSTANT_SYSTEM = const JavaScriptConstantSystem(); | |
8 | |
9 class JavaScriptBitNotOperation extends BitNotOperation { | |
10 const JavaScriptBitNotOperation(); | |
11 | |
12 ConstantValue fold(ConstantValue constant) { | |
13 if (JAVA_SCRIPT_CONSTANT_SYSTEM.isInt(constant)) { | |
14 // In JavaScript we don't check for -0 and treat it as if it was zero. | |
15 if (constant.isMinusZero) constant = DART_CONSTANT_SYSTEM.createInt(0); | |
16 IntConstantValue intConstant = constant; | |
17 // We convert the result of bit-operations to 32 bit unsigned integers. | |
18 return | |
19 JAVA_SCRIPT_CONSTANT_SYSTEM.createInt32(~intConstant.primitiveValue); | |
20 } | |
21 return null; | |
22 } | |
23 } | |
24 | |
25 /** | |
26 * In JavaScript we truncate the result to an unsigned 32 bit integer. Also, -0 | |
27 * is treated as if it was the integer 0. | |
28 */ | |
29 class JavaScriptBinaryBitOperation implements BinaryOperation { | |
30 final BinaryBitOperation dartBitOperation; | |
31 | |
32 const JavaScriptBinaryBitOperation(this.dartBitOperation); | |
33 | |
34 String get name => dartBitOperation.name; | |
35 | |
36 ConstantValue fold(ConstantValue left, ConstantValue right) { | |
37 // In JavaScript we don't check for -0 and treat it as if it was zero. | |
38 if (left.isMinusZero) left = DART_CONSTANT_SYSTEM.createInt(0); | |
39 if (right.isMinusZero) right = DART_CONSTANT_SYSTEM.createInt(0); | |
40 IntConstantValue result = dartBitOperation.fold(left, right); | |
41 if (result != null) { | |
42 // We convert the result of bit-operations to 32 bit unsigned integers. | |
43 return JAVA_SCRIPT_CONSTANT_SYSTEM.createInt32(result.primitiveValue); | |
44 } | |
45 return result; | |
46 } | |
47 | |
48 apply(left, right) => dartBitOperation.apply(left, right); | |
49 } | |
50 | |
51 class JavaScriptShiftRightOperation extends JavaScriptBinaryBitOperation { | |
52 const JavaScriptShiftRightOperation() : super(const ShiftRightOperation()); | |
53 | |
54 ConstantValue fold(ConstantValue left, ConstantValue right) { | |
55 // Truncate the input value to 32 bits if necessary. | |
56 if (left.isInt) { | |
57 IntConstantValue intConstant = left; | |
58 int value = intConstant.primitiveValue; | |
59 int truncatedValue = value & JAVA_SCRIPT_CONSTANT_SYSTEM.BITS32; | |
60 if (value < 0) { | |
61 // Sign-extend if the input was negative. The current semantics don't | |
62 // make much sense, since we only look at bit 31. | |
63 // TODO(floitsch): we should treat the input to right shifts as | |
64 // unsigned. | |
65 | |
66 // A 32 bit complement-two value x can be computed by: | |
67 // x_u - 2^32 (where x_u is its unsigned representation). | |
68 // Example: 0xFFFFFFFF - 0x100000000 => -1. | |
69 // We simply and with the sign-bit and multiply by two. If the sign-bit | |
70 // was set, then the result is 0. Otherwise it will become 2^32. | |
71 final int SIGN_BIT = 0x80000000; | |
72 truncatedValue -= 2 * (truncatedValue & SIGN_BIT); | |
73 } | |
74 if (value != truncatedValue) { | |
75 left = DART_CONSTANT_SYSTEM.createInt(truncatedValue); | |
76 } | |
77 } | |
78 return super.fold(left, right); | |
79 } | |
80 } | |
81 | |
82 class JavaScriptNegateOperation implements UnaryOperation { | |
83 final NegateOperation dartNegateOperation = const NegateOperation(); | |
84 | |
85 const JavaScriptNegateOperation(); | |
86 | |
87 String get name => dartNegateOperation.name; | |
88 | |
89 ConstantValue fold(ConstantValue constant) { | |
90 if (constant.isInt) { | |
91 IntConstantValue intConstant = constant; | |
92 if (intConstant.primitiveValue == 0) { | |
93 return JAVA_SCRIPT_CONSTANT_SYSTEM.createDouble(-0.0); | |
94 } | |
95 } | |
96 return dartNegateOperation.fold(constant); | |
97 } | |
98 } | |
99 | |
100 class JavaScriptBinaryArithmeticOperation implements BinaryOperation { | |
101 final BinaryOperation dartArithmeticOperation; | |
102 | |
103 const JavaScriptBinaryArithmeticOperation(this.dartArithmeticOperation); | |
104 | |
105 String get name => dartArithmeticOperation.name; | |
106 | |
107 ConstantValue fold(ConstantValue left, ConstantValue right) { | |
108 ConstantValue result = dartArithmeticOperation.fold(left, right); | |
109 if (result == null) return result; | |
110 return JAVA_SCRIPT_CONSTANT_SYSTEM.convertToJavaScriptConstant(result); | |
111 } | |
112 | |
113 apply(left, right) => dartArithmeticOperation.apply(left, right); | |
114 } | |
115 | |
116 class JavaScriptIdentityOperation implements BinaryOperation { | |
117 final IdentityOperation dartIdentityOperation = const IdentityOperation(); | |
118 | |
119 const JavaScriptIdentityOperation(); | |
120 | |
121 String get name => dartIdentityOperation.name; | |
122 | |
123 BoolConstantValue fold(ConstantValue left, ConstantValue right) { | |
124 BoolConstantValue result = dartIdentityOperation.fold(left, right); | |
125 if (result == null || result.primitiveValue) return result; | |
126 // In JavaScript -0.0 === 0 and all doubles are equal to their integer | |
127 // values. Furthermore NaN !== NaN. | |
128 if (left.isNum && right.isNum) { | |
129 NumConstantValue leftNum = left; | |
130 NumConstantValue rightNum = right; | |
131 double leftDouble = leftNum.primitiveValue.toDouble(); | |
132 double rightDouble = rightNum.primitiveValue.toDouble(); | |
133 return new BoolConstantValue(leftDouble == rightDouble); | |
134 } | |
135 return result; | |
136 } | |
137 | |
138 apply(left, right) => identical(left, right); | |
139 } | |
140 | |
141 /** | |
142 * Constant system following the semantics for Dart code that has been | |
143 * compiled to JavaScript. | |
144 */ | |
145 class JavaScriptConstantSystem extends ConstantSystem { | |
146 final int BITS31 = 0x8FFFFFFF; | |
147 final int BITS32 = 0xFFFFFFFF; | |
148 | |
149 final add = const JavaScriptBinaryArithmeticOperation(const AddOperation()); | |
150 final bitAnd = const JavaScriptBinaryBitOperation(const BitAndOperation()); | |
151 final bitNot = const JavaScriptBitNotOperation(); | |
152 final bitOr = const JavaScriptBinaryBitOperation(const BitOrOperation()); | |
153 final bitXor = const JavaScriptBinaryBitOperation(const BitXorOperation()); | |
154 final booleanAnd = const BooleanAndOperation(); | |
155 final booleanOr = const BooleanOrOperation(); | |
156 final divide = | |
157 const JavaScriptBinaryArithmeticOperation(const DivideOperation()); | |
158 final equal = const EqualsOperation(); | |
159 final greaterEqual = const GreaterEqualOperation(); | |
160 final greater = const GreaterOperation(); | |
161 final identity = const JavaScriptIdentityOperation(); | |
162 final lessEqual = const LessEqualOperation(); | |
163 final less = const LessOperation(); | |
164 final modulo = | |
165 const JavaScriptBinaryArithmeticOperation(const ModuloOperation()); | |
166 final multiply = | |
167 const JavaScriptBinaryArithmeticOperation(const MultiplyOperation()); | |
168 final negate = const JavaScriptNegateOperation(); | |
169 final not = const NotOperation(); | |
170 final shiftLeft = | |
171 const JavaScriptBinaryBitOperation(const ShiftLeftOperation()); | |
172 final shiftRight = const JavaScriptShiftRightOperation(); | |
173 final subtract = | |
174 const JavaScriptBinaryArithmeticOperation(const SubtractOperation()); | |
175 final truncatingDivide = const JavaScriptBinaryArithmeticOperation( | |
176 const TruncatingDivideOperation()); | |
177 final codeUnitAt = const CodeUnitAtRuntimeOperation(); | |
178 | |
179 const JavaScriptConstantSystem(); | |
180 | |
181 /** | |
182 * Returns true if [value] will turn into NaN or infinity | |
183 * at runtime. | |
184 */ | |
185 bool integerBecomesNanOrInfinity(int value) { | |
186 double doubleValue = value.toDouble(); | |
187 return doubleValue.isNaN || doubleValue.isInfinite; | |
188 } | |
189 | |
190 NumConstantValue convertToJavaScriptConstant(NumConstantValue constant) { | |
191 if (constant.isInt) { | |
192 IntConstantValue intConstant = constant; | |
193 int intValue = intConstant.primitiveValue; | |
194 if (integerBecomesNanOrInfinity(intValue)) { | |
195 return new DoubleConstantValue(intValue.toDouble()); | |
196 } | |
197 // If the integer loses precision with JavaScript numbers, use | |
198 // the floored version JavaScript will use. | |
199 int floorValue = intValue.toDouble().floor().toInt(); | |
200 if (floorValue != intValue) { | |
201 return new IntConstantValue(floorValue); | |
202 } | |
203 } else if (constant.isDouble) { | |
204 DoubleConstantValue doubleResult = constant; | |
205 double doubleValue = doubleResult.primitiveValue; | |
206 if (!doubleValue.isInfinite && !doubleValue.isNaN && | |
207 !constant.isMinusZero) { | |
208 int intValue = doubleValue.truncate(); | |
209 if (intValue == doubleValue) { | |
210 return new IntConstantValue(intValue); | |
211 } | |
212 } | |
213 } | |
214 return constant; | |
215 } | |
216 | |
217 NumConstantValue createInt(int i) | |
218 => convertToJavaScriptConstant(new IntConstantValue(i)); | |
219 NumConstantValue createInt32(int i) => new IntConstantValue(i & BITS32); | |
220 NumConstantValue createDouble(double d) | |
221 => convertToJavaScriptConstant(new DoubleConstantValue(d)); | |
222 StringConstantValue createString(DartString string) { | |
223 return new StringConstantValue(string); | |
224 } | |
225 BoolConstantValue createBool(bool value) => new BoolConstantValue(value); | |
226 NullConstantValue createNull() => new NullConstantValue(); | |
227 | |
228 // Integer checks don't verify that the number is not -0.0. | |
229 bool isInt(ConstantValue constant) => constant.isInt || constant.isMinusZero; | |
230 bool isDouble(ConstantValue constant) | |
231 => constant.isDouble && !constant.isMinusZero; | |
232 bool isString(ConstantValue constant) => constant.isString; | |
233 bool isBool(ConstantValue constant) => constant.isBool; | |
234 bool isNull(ConstantValue constant) => constant.isNull; | |
235 | |
236 bool isSubtype(Compiler compiler, DartType s, DartType t) { | |
237 // At runtime, an integer is both an integer and a double: the | |
238 // integer type check is Math.floor, which will return true only | |
239 // for real integers, and our double type check is 'typeof number' | |
240 // which will return true for both integers and doubles. | |
241 if (s.element == compiler.intClass && t.element == compiler.doubleClass) { | |
242 return true; | |
243 } | |
244 return compiler.types.isSubtype(s, t); | |
245 } | |
246 | |
247 MapConstantValue createMap(Compiler compiler, | |
248 InterfaceType sourceType, | |
249 List<ConstantValue> keys, | |
250 List<ConstantValue> values) { | |
251 JavaScriptBackend backend = compiler.backend; | |
252 | |
253 bool onlyStringKeys = true; | |
254 ConstantValue protoValue = null; | |
255 for (int i = 0; i < keys.length ; i++) { | |
256 var key = keys[i]; | |
257 if (key.isString) { | |
258 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { | |
259 protoValue = values[i]; | |
260 } | |
261 } else { | |
262 onlyStringKeys = false; | |
263 // Don't handle __proto__ values specially in the general map case. | |
264 protoValue = null; | |
265 break; | |
266 } | |
267 } | |
268 | |
269 bool hasProtoKey = (protoValue != null); | |
270 DartType keysType; | |
271 if (sourceType.treatAsRaw) { | |
272 keysType = compiler.listClass.rawType; | |
273 } else { | |
274 List<DartType> arguments = <DartType>[sourceType.typeArguments.first]; | |
275 keysType = new InterfaceType(compiler.listClass, arguments); | |
276 } | |
277 ListConstantValue keysList = new ListConstantValue(keysType, keys); | |
278 String className = onlyStringKeys | |
279 ? (hasProtoKey ? JavaScriptMapConstant.DART_PROTO_CLASS | |
280 : JavaScriptMapConstant.DART_STRING_CLASS) | |
281 : JavaScriptMapConstant.DART_GENERAL_CLASS; | |
282 ClassElement classElement = backend.jsHelperLibrary.find(className); | |
283 classElement.ensureResolved(compiler); | |
284 List<DartType> typeArgument = sourceType.typeArguments; | |
285 InterfaceType type; | |
286 if (sourceType.treatAsRaw) { | |
287 type = classElement.rawType; | |
288 } else { | |
289 type = new InterfaceType(classElement, typeArgument); | |
290 } | |
291 return new JavaScriptMapConstant( | |
292 type, keysList, values, protoValue, onlyStringKeys); | |
293 | |
294 } | |
295 } | |
296 | |
297 class JavaScriptMapConstant extends MapConstantValue { | |
298 /** | |
299 * The [PROTO_PROPERTY] must not be used as normal property in any JavaScript | |
300 * object. It would change the prototype chain. | |
301 */ | |
302 static const LiteralDartString PROTO_PROPERTY = | |
303 const LiteralDartString("__proto__"); | |
304 | |
305 /** The dart class implementing constant map literals. */ | |
306 static const String DART_CLASS = "ConstantMap"; | |
307 static const String DART_STRING_CLASS = "ConstantStringMap"; | |
308 static const String DART_PROTO_CLASS = "ConstantProtoMap"; | |
309 static const String DART_GENERAL_CLASS = "GeneralConstantMap"; | |
310 static const String LENGTH_NAME = "length"; | |
311 static const String JS_OBJECT_NAME = "_jsObject"; | |
312 static const String KEYS_NAME = "_keys"; | |
313 static const String PROTO_VALUE = "_protoValue"; | |
314 static const String JS_DATA_NAME = "_jsData"; | |
315 | |
316 final ListConstantValue keyList; | |
317 final ConstantValue protoValue; | |
318 final bool onlyStringKeys; | |
319 | |
320 JavaScriptMapConstant(InterfaceType type, | |
321 ListConstantValue keyList, | |
322 List<ConstantValue> values, | |
323 this.protoValue, | |
324 this.onlyStringKeys) | |
325 : this.keyList = keyList, | |
326 super(type, keyList.entries, values); | |
327 bool get isMap => true; | |
328 | |
329 TypeMask computeMask(Compiler compiler) { | |
330 return compiler.typesTask.constMapType; | |
331 } | |
332 | |
333 List<ConstantValue> getDependencies() { | |
334 List<ConstantValue> result = <ConstantValue>[]; | |
335 if (onlyStringKeys) { | |
336 result.add(keyList); | |
337 } else { | |
338 // Add the keys individually to avoid generating an unused list constant | |
339 // for the keys. | |
340 result.addAll(keys); | |
341 } | |
342 result.addAll(values); | |
343 return result; | |
344 } | |
345 } | |
OLD | NEW |