1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; -*- |
2 |
* vim: set ts=8 sw=4 et tw=99: |
3 |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
5 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
6 |
* |
7 |
* The contents of this file are subject to the Mozilla Public License Version |
8 |
* 1.1 (the "License"); you may not use this file except in compliance with |
9 |
* the License. You may obtain a copy of the License at |
10 |
* http://www.mozilla.org/MPL/ |
11 |
* |
12 |
* Software distributed under the License is distributed on an "AS IS" basis, |
13 |
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
14 |
* for the specific language governing rights and limitations under the |
15 |
* License. |
16 |
* |
17 |
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released |
18 |
* May 28, 2008. |
19 |
* |
20 |
* The Initial Developer of the Original Code is |
21 |
* Andreas Gal <gal@mozilla.com> |
22 |
* |
23 |
* Contributor(s): |
24 |
* Brendan Eich <brendan@mozilla.org> |
25 |
* Mike Shaver <shaver@mozilla.org> |
26 |
* David Anderson <danderson@mozilla.com> |
27 |
* |
28 |
* Alternatively, the contents of this file may be used under the terms of |
29 |
* either of the GNU General Public License Version 2 or later (the "GPL"), |
30 |
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
31 |
* in which case the provisions of the GPL or the LGPL are applicable instead |
32 |
* of those above. If you wish to allow use of your version of this file only |
33 |
* under the terms of either the GPL or the LGPL, and not to allow others to |
34 |
* use your version of this file under the terms of the MPL, indicate your |
35 |
* decision by deleting the provisions above and replace them with the notice |
36 |
* and other provisions required by the GPL or the LGPL. If you do not delete |
37 |
* the provisions above, a recipient may use your version of this file under |
38 |
* the terms of any one of the MPL, the GPL or the LGPL. |
39 |
* |
40 |
* ***** END LICENSE BLOCK ***** */ |
41 |
|
42 |
#include "jsstddef.h" |
43 |
#include <math.h> |
44 |
|
45 |
#include "jsapi.h" |
46 |
#include "jsarray.h" |
47 |
#include "jsbool.h" |
48 |
#include "jscntxt.h" |
49 |
#include "jsgc.h" |
50 |
#include "jsiter.h" |
51 |
#include "jslibmath.h" |
52 |
#include "jsmath.h" |
53 |
#include "jsnum.h" |
54 |
#include "prmjtime.h" |
55 |
#include "jsdate.h" |
56 |
#include "jsscope.h" |
57 |
#include "jsstr.h" |
58 |
#include "jsbuiltins.h" |
59 |
#include "jstracer.h" |
60 |
|
61 |
using namespace avmplus; |
62 |
using namespace nanojit; |
63 |
|
64 |
extern jsdouble js_NaN; |
65 |
|
66 |
JS_FRIEND_API(void) |
67 |
js_SetTraceableNativeFailed(JSContext *cx) |
68 |
{ |
69 |
js_SetBuiltinError(cx); |
70 |
} |
71 |
|
72 |
/* |
73 |
* NB: bool FASTCALL is not compatible with Nanojit's calling convention usage. |
74 |
* Do not use bool FASTCALL, use JSBool only! |
75 |
*/ |
76 |
|
77 |
jsdouble FASTCALL |
78 |
js_dmod(jsdouble a, jsdouble b) |
79 |
{ |
80 |
if (b == 0.0) { |
81 |
jsdpun u; |
82 |
u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; |
83 |
u.s.lo = 0xffffffff; |
84 |
return u.d; |
85 |
} |
86 |
jsdouble r; |
87 |
#ifdef XP_WIN |
88 |
/* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ |
89 |
if (JSDOUBLE_IS_FINITE(a) && JSDOUBLE_IS_INFINITE(b)) |
90 |
r = a; |
91 |
else |
92 |
#endif |
93 |
r = fmod(a, b); |
94 |
return r; |
95 |
} |
96 |
|
97 |
int32 FASTCALL |
98 |
js_imod(int32 a, int32 b) |
99 |
{ |
100 |
if (a < 0 || b <= 0) |
101 |
return -1; |
102 |
int r = a % b; |
103 |
return r; |
104 |
} |
105 |
|
106 |
/* The following boxing/unboxing primitives we can't emit inline because |
107 |
they either interact with the GC and depend on Spidermonkey's 32-bit |
108 |
integer representation. */ |
109 |
|
110 |
jsval FASTCALL |
111 |
js_BoxDouble(JSContext* cx, jsdouble d) |
112 |
{ |
113 |
int32 i; |
114 |
if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) |
115 |
return INT_TO_JSVAL(i); |
116 |
JS_ASSERT(JS_ON_TRACE(cx)); |
117 |
jsval v; /* not rooted but ok here because we know GC won't run */ |
118 |
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
119 |
return JSVAL_ERROR_COOKIE; |
120 |
return v; |
121 |
} |
122 |
|
123 |
jsval FASTCALL |
124 |
js_BoxInt32(JSContext* cx, int32 i) |
125 |
{ |
126 |
if (JS_LIKELY(INT_FITS_IN_JSVAL(i))) |
127 |
return INT_TO_JSVAL(i); |
128 |
JS_ASSERT(JS_ON_TRACE(cx)); |
129 |
jsval v; /* not rooted but ok here because we know GC won't run */ |
130 |
jsdouble d = (jsdouble)i; |
131 |
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
132 |
return JSVAL_ERROR_COOKIE; |
133 |
return v; |
134 |
} |
135 |
|
136 |
jsdouble FASTCALL |
137 |
js_UnboxDouble(jsval v) |
138 |
{ |
139 |
if (JS_LIKELY(JSVAL_IS_INT(v))) |
140 |
return (jsdouble)JSVAL_TO_INT(v); |
141 |
return *JSVAL_TO_DOUBLE(v); |
142 |
} |
143 |
|
144 |
int32 FASTCALL |
145 |
js_UnboxInt32(jsval v) |
146 |
{ |
147 |
if (JS_LIKELY(JSVAL_IS_INT(v))) |
148 |
return JSVAL_TO_INT(v); |
149 |
return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v)); |
150 |
} |
151 |
|
152 |
int32 FASTCALL |
153 |
js_DoubleToInt32(jsdouble d) |
154 |
{ |
155 |
return js_DoubleToECMAInt32(d); |
156 |
} |
157 |
|
158 |
uint32 FASTCALL |
159 |
js_DoubleToUint32(jsdouble d) |
160 |
{ |
161 |
return js_DoubleToECMAUint32(d); |
162 |
} |
163 |
|
164 |
jsdouble FASTCALL |
165 |
js_StringToNumber(JSContext* cx, JSString* str) |
166 |
{ |
167 |
const jschar* bp; |
168 |
const jschar* end; |
169 |
const jschar* ep; |
170 |
jsdouble d; |
171 |
|
172 |
JSSTRING_CHARS_AND_END(str, bp, end); |
173 |
if ((!js_strtod(cx, bp, end, &ep, &d) || |
174 |
js_SkipWhiteSpace(ep, end) != end) && |
175 |
(!js_strtointeger(cx, bp, end, &ep, 0, &d) || |
176 |
js_SkipWhiteSpace(ep, end) != end)) { |
177 |
return js_NaN; |
178 |
} |
179 |
return d; |
180 |
} |
181 |
|
182 |
int32 FASTCALL |
183 |
js_StringToInt32(JSContext* cx, JSString* str) |
184 |
{ |
185 |
const jschar* bp; |
186 |
const jschar* end; |
187 |
const jschar* ep; |
188 |
jsdouble d; |
189 |
|
190 |
JSSTRING_CHARS_AND_END(str, bp, end); |
191 |
if ((!js_strtod(cx, bp, end, &ep, &d) || |
192 |
js_SkipWhiteSpace(ep, end) != end) && |
193 |
(!js_strtointeger(cx, bp, end, &ep, 0, &d) || |
194 |
js_SkipWhiteSpace(ep, end) != end)) { |
195 |
return 0; |
196 |
} |
197 |
return js_DoubleToECMAInt32(d); |
198 |
} |
199 |
|
200 |
SideExit* FASTCALL |
201 |
js_CallTree(InterpState* state, Fragment* f) |
202 |
{ |
203 |
union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u; |
204 |
|
205 |
u.code = f->code(); |
206 |
JS_ASSERT(u.code); |
207 |
|
208 |
GuardRecord* rec; |
209 |
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) |
210 |
SIMULATE_FASTCALL(rec, state, NULL, u.func); |
211 |
#else |
212 |
rec = u.func(state, NULL); |
213 |
#endif |
214 |
VMSideExit* lr = (VMSideExit*)rec->exit; |
215 |
|
216 |
if (lr->exitType == NESTED_EXIT) { |
217 |
/* This only occurs once a tree call guard mismatches and we unwind the tree call stack. |
218 |
We store the first (innermost) tree call guard in state and we will try to grow |
219 |
the outer tree the failing call was in starting at that guard. */ |
220 |
if (!state->lastTreeCallGuard) { |
221 |
state->lastTreeCallGuard = lr; |
222 |
FrameInfo** rp = (FrameInfo**)state->rp; |
223 |
state->rpAtLastTreeCall = rp + lr->calldepth; |
224 |
} |
225 |
} else { |
226 |
/* If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard |
227 |
with that guard. If we mismatch on a tree call guard, this will contain the last |
228 |
non-nested guard we encountered, which is the innermost loop or branch guard. */ |
229 |
state->lastTreeExitGuard = lr; |
230 |
} |
231 |
|
232 |
return lr; |
233 |
} |
234 |
|
235 |
JSBool FASTCALL |
236 |
js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) |
237 |
{ |
238 |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
239 |
JS_ASSERT(SPROP_HAS_STUB_SETTER(sprop)); |
240 |
|
241 |
JS_LOCK_OBJ(cx, obj); |
242 |
|
243 |
JSScope* scope = OBJ_SCOPE(obj); |
244 |
uint32 slot; |
245 |
if (scope->object == obj) { |
246 |
JS_ASSERT(!SCOPE_HAS_PROPERTY(scope, sprop)); |
247 |
} else { |
248 |
scope = js_GetMutableScope(cx, obj); |
249 |
if (!scope) |
250 |
goto exit_trace; |
251 |
} |
252 |
|
253 |
slot = sprop->slot; |
254 |
if (!scope->table && sprop->parent == scope->lastProp && slot == scope->freeslot) { |
255 |
if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { |
256 |
JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); |
257 |
++scope->freeslot; |
258 |
} else { |
259 |
if (!js_AllocSlot(cx, obj, &slot)) |
260 |
goto exit_trace; |
261 |
|
262 |
if (slot != sprop->slot) { |
263 |
js_FreeSlot(cx, obj, slot); |
264 |
goto exit_trace; |
265 |
} |
266 |
} |
267 |
|
268 |
js_ExtendScopeShape(cx, scope, sprop); |
269 |
++scope->entryCount; |
270 |
scope->lastProp = sprop; |
271 |
} else { |
272 |
JSScopeProperty *sprop2 = js_AddScopeProperty(cx, scope, sprop->id, |
273 |
sprop->getter, |
274 |
sprop->setter, |
275 |
SPROP_INVALID_SLOT, |
276 |
sprop->attrs, |
277 |
sprop->flags, |
278 |
sprop->shortid); |
279 |
if (sprop2 != sprop) |
280 |
goto exit_trace; |
281 |
} |
282 |
|
283 |
if (js_IsPropertyCacheDisabled(cx)) |
284 |
goto exit_trace; |
285 |
|
286 |
JS_UNLOCK_SCOPE(cx, scope); |
287 |
return JS_TRUE; |
288 |
|
289 |
exit_trace: |
290 |
JS_UNLOCK_SCOPE(cx, scope); |
291 |
return JS_FALSE; |
292 |
} |
293 |
|
294 |
static JSBool |
295 |
HasProperty(JSContext* cx, JSObject* obj, jsid id) |
296 |
{ |
297 |
// Check that we know how the lookup op will behave. |
298 |
for (JSObject* pobj = obj; pobj; pobj = OBJ_GET_PROTO(cx, pobj)) { |
299 |
if (pobj->map->ops->lookupProperty != js_LookupProperty) |
300 |
return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID); |
301 |
JSClass* clasp = OBJ_GET_CLASS(cx, pobj); |
302 |
if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass) |
303 |
return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID); |
304 |
} |
305 |
|
306 |
JSObject* obj2; |
307 |
JSProperty* prop; |
308 |
if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop)) |
309 |
return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID); |
310 |
if (prop) |
311 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
312 |
return prop != NULL; |
313 |
} |
314 |
|
315 |
JSBool FASTCALL |
316 |
js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr) |
317 |
{ |
318 |
jsid id; |
319 |
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id)) |
320 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
321 |
|
322 |
return HasProperty(cx, obj, id); |
323 |
} |
324 |
|
325 |
JSBool FASTCALL |
326 |
js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index) |
327 |
{ |
328 |
jsid id; |
329 |
if (!js_Int32ToId(cx, index, &id)) |
330 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
331 |
|
332 |
return HasProperty(cx, obj, id); |
333 |
} |
334 |
|
335 |
jsval FASTCALL |
336 |
js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) |
337 |
{ |
338 |
JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop)); |
339 |
jsval v; |
340 |
if (!js_GetSprop(cx, sprop, obj, &v)) |
341 |
return JSVAL_ERROR_COOKIE; |
342 |
return v; |
343 |
} |
344 |
|
345 |
JSString* FASTCALL |
346 |
js_TypeOfObject(JSContext* cx, JSObject* obj) |
347 |
{ |
348 |
JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj)); |
349 |
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
350 |
} |
351 |
|
352 |
JSString* FASTCALL |
353 |
js_TypeOfBoolean(JSContext* cx, int32 unboxed) |
354 |
{ |
355 |
/* Watch out for pseudo-booleans. */ |
356 |
jsval boxed = PSEUDO_BOOLEAN_TO_JSVAL(unboxed); |
357 |
JS_ASSERT(JSVAL_IS_VOID(boxed) || JSVAL_IS_BOOLEAN(boxed)); |
358 |
JSType type = JS_TypeOfValue(cx, boxed); |
359 |
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
360 |
} |
361 |
|
362 |
jsdouble FASTCALL |
363 |
js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed) |
364 |
{ |
365 |
if (unboxed == JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)) |
366 |
return js_NaN; |
367 |
JS_ASSERT(unboxed == JS_TRUE || unboxed == JS_FALSE); |
368 |
return unboxed; |
369 |
} |
370 |
|
371 |
JSString* FASTCALL |
372 |
js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed) |
373 |
{ |
374 |
JS_ASSERT(uint32(unboxed) <= 2); |
375 |
return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[unboxed]); |
376 |
} |
377 |
|
378 |
JSObject* FASTCALL |
379 |
js_Arguments(JSContext* cx) |
380 |
{ |
381 |
return NULL; |
382 |
} |
383 |
|
384 |
JSObject* FASTCALL |
385 |
js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent) |
386 |
{ |
387 |
JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); |
388 |
JS_ASSERT(HAS_FUNCTION_CLASS(proto)); |
389 |
JS_ASSERT(JS_ON_TRACE(cx)); |
390 |
|
391 |
JSFunction *fun = (JSFunction*) funobj; |
392 |
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); |
393 |
|
394 |
JSObject* closure = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); |
395 |
if (!closure) |
396 |
return NULL; |
397 |
|
398 |
js_HoldScope(OBJ_SCOPE(proto)); |
399 |
closure->map = proto->map; |
400 |
closure->classword = jsuword(&js_FunctionClass); |
401 |
closure->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
402 |
closure->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); |
403 |
closure->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun); |
404 |
for (unsigned i = JSSLOT_PRIVATE + 1; i != JS_INITIAL_NSLOTS; ++i) |
405 |
closure->fslots[i] = JSVAL_VOID; |
406 |
closure->dslots = NULL; |
407 |
return closure; |
408 |
} |
409 |
|
410 |
#define BUILTIN1 JS_DEFINE_CALLINFO_1 |
411 |
#define BUILTIN2 JS_DEFINE_CALLINFO_2 |
412 |
#define BUILTIN3 JS_DEFINE_CALLINFO_3 |
413 |
#define BUILTIN4 JS_DEFINE_CALLINFO_4 |
414 |
#define BUILTIN5 JS_DEFINE_CALLINFO_5 |
415 |
#include "builtins.tbl" |