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 | |
178 const JavaScriptConstantSystem(); | |
179 | |
180 /** | |
181 * Returns true if [value] will turn into NaN or infinity | |
182 * at runtime. | |
183 */ | |
184 bool integerBecomesNanOrInfinity(int value) { | |
185 double doubleValue = value.toDouble(); | |
186 return doubleValue.isNaN || doubleValue.isInfinite; | |
187 } | |
188 | |
189 NumConstantValue convertToJavaScriptConstant(NumConstantValue constant) { | |
190 if (constant.isInt) { | |
191 IntConstantValue intConstant = constant; | |
192 int intValue = intConstant.primitiveValue; | |
193 if (integerBecomesNanOrInfinity(intValue)) { | |
194 return new DoubleConstantValue(intValue.toDouble()); | |
195 } | |
196 // If the integer loses precision with JavaScript numbers, use | |
197 // the floored version JavaScript will use. | |
198 int floorValue = intValue.toDouble().floor().toInt(); | |
199 if (floorValue != intValue) { | |
200 return new IntConstantValue(floorValue); | |
201 } | |
202 } else if (constant.isDouble) { | |
203 DoubleConstantValue doubleResult = constant; | |
204 double doubleValue = doubleResult.primitiveValue; | |
205 if (!doubleValue.isInfinite && !doubleValue.isNaN && | |
206 !constant.isMinusZero) { | |
207 int intValue = doubleValue.truncate(); | |
208 if (intValue == doubleValue) { | |
209 return new IntConstantValue(intValue); | |
210 } | |
211 } | |
212 } | |
213 return constant; | |
214 } | |
215 | |
216 NumConstantValue createInt(int i) | |
217 => convertToJavaScriptConstant(new IntConstantValue(i)); | |
218 NumConstantValue createInt32(int i) => new IntConstantValue(i & BITS32); | |
219 NumConstantValue createDouble(double d) | |
220 => convertToJavaScriptConstant(new DoubleConstantValue(d)); | |
221 StringConstantValue createString(DartString string) { | |
222 return new StringConstantValue(string); | |
223 } | |
224 BoolConstantValue createBool(bool value) => new BoolConstantValue(value); | |
225 NullConstantValue createNull() => new NullConstantValue(); | |
226 | |
227 // Integer checks don't verify that the number is not -0.0. | |
228 bool isInt(ConstantValue constant) => constant.isInt || constant.isMinusZero; | |
229 bool isDouble(ConstantValue constant) | |
230 => constant.isDouble && !constant.isMinusZero; | |
231 bool isString(ConstantValue constant) => constant.isString; | |
232 bool isBool(ConstantValue constant) => constant.isBool; | |
233 bool isNull(ConstantValue constant) => constant.isNull; | |
234 | |
235 bool isSubtype(Compiler compiler, DartType s, DartType t) { | |
236 // At runtime, an integer is both an integer and a double: the | |
237 // integer type check is Math.floor, which will return true only | |
238 // for real integers, and our double type check is 'typeof number' | |
239 // which will return true for both integers and doubles. | |
240 if (s.element == compiler.intClass && t.element == compiler.doubleClass) { | |
241 return true; | |
242 } | |
243 return compiler.types.isSubtype(s, t); | |
244 } | |
245 | |
246 MapConstantValue createMap(Compiler compiler, | |
247 InterfaceType sourceType, | |
248 List<ConstantValue> keys, | |
249 List<ConstantValue> values) { | |
250 JavaScriptBackend backend = compiler.backend; | |
251 | |
252 bool onlyStringKeys = true; | |
253 ConstantValue protoValue = null; | |
254 for (int i = 0; i < keys.length ; i++) { | |
255 var key = keys[i]; | |
256 if (key.isString) { | |
257 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { | |
258 protoValue = values[i]; | |
259 } | |
260 } else { | |
261 onlyStringKeys = false; | |
262 // Don't handle __proto__ values specially in the general map case. | |
263 protoValue = null; | |
264 break; | |
265 } | |
266 } | |
267 | |
268 bool hasProtoKey = (protoValue != null); | |
269 DartType keysType; | |
270 if (sourceType.treatAsRaw) { | |
271 keysType = compiler.listClass.rawType; | |
272 } else { | |
273 List<DartType> arguments = <DartType>[sourceType.typeArguments.first]; | |
274 keysType = new InterfaceType(compiler.listClass, arguments); | |
275 } | |
276 ListConstantValue keysList = new ListConstantValue(keysType, keys); | |
277 String className = onlyStringKeys | |
278 ? (hasProtoKey ? JavaScriptMapConstant.DART_PROTO_CLASS | |
279 : JavaScriptMapConstant.DART_STRING_CLASS) | |
280 : JavaScriptMapConstant.DART_GENERAL_CLASS; | |
281 ClassElement classElement = backend.jsHelperLibrary.find(className); | |
282 classElement.ensureResolved(compiler); | |
283 List<DartType> typeArgument = sourceType.typeArguments; | |
284 InterfaceType type; | |
285 if (sourceType.treatAsRaw) { | |
286 type = classElement.rawType; | |
287 } else { | |
288 type = new InterfaceType(classElement, typeArgument); | |
289 } | |
290 return new JavaScriptMapConstant( | |
291 type, keysList, values, protoValue, onlyStringKeys); | |
292 | |
293 } | |
294 } | |
295 | |
296 class JavaScriptMapConstant extends MapConstantValue { | |
297 /** | |
298 * The [PROTO_PROPERTY] must not be used as normal property in any JavaScript | |
299 * object. It would change the prototype chain. | |
300 */ | |
301 static const LiteralDartString PROTO_PROPERTY = | |
302 const LiteralDartString("__proto__"); | |
303 | |
304 /** The dart class implementing constant map literals. */ | |
305 static const String DART_CLASS = "ConstantMap"; | |
306 static const String DART_STRING_CLASS = "ConstantStringMap"; | |
307 static const String DART_PROTO_CLASS = "ConstantProtoMap"; | |
308 static const String DART_GENERAL_CLASS = "GeneralConstantMap"; | |
309 static const String LENGTH_NAME = "length"; | |
310 static const String JS_OBJECT_NAME = "_jsObject"; | |
311 static const String KEYS_NAME = "_keys"; | |
312 static const String PROTO_VALUE = "_protoValue"; | |
313 static const String JS_DATA_NAME = "_jsData"; | |
314 | |
315 final ListConstantValue keyList; | |
316 final ConstantValue protoValue; | |
317 final bool onlyStringKeys; | |
318 | |
319 JavaScriptMapConstant(InterfaceType type, | |
320 ListConstantValue keyList, | |
321 List<ConstantValue> values, | |
322 this.protoValue, | |
323 this.onlyStringKeys) | |
324 : this.keyList = keyList, | |
325 super(type, keyList.entries, values); | |
326 bool get isMap => true; | |
327 | |
328 TypeMask computeMask(Compiler compiler) { | |
329 return compiler.typesTask.constMapType; | |
330 } | |
331 | |
332 List<ConstantValue> getDependencies() { | |
333 List<ConstantValue> result = <ConstantValue>[]; | |
334 if (onlyStringKeys) { | |
335 result.add(keyList); | |
336 } else { | |
337 // Add the keys individually to avoid generating an unused list constant | |
338 // for the keys. | |
339 result.addAll(keys); | |
340 } | |
341 result.addAll(values); | |
342 return result; | |
343 } | |
344 } | |
OLD | NEW |