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 SpiderMonkey global object code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Jeff Walden <jwalden+code@mit.edu> (original author)
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "GlobalObject.h"
42 :
43 : #include "jscntxt.h"
44 : #include "jsexn.h"
45 : #include "jsmath.h"
46 : #include "json.h"
47 : #include "jsweakmap.h"
48 :
49 : #include "builtin/MapObject.h"
50 : #include "builtin/RegExp.h"
51 : #include "frontend/BytecodeEmitter.h"
52 : #include "vm/GlobalObject-inl.h"
53 :
54 : #include "jsobjinlines.h"
55 : #include "vm/RegExpObject-inl.h"
56 : #include "vm/RegExpStatics-inl.h"
57 :
58 : #ifdef JS_METHODJIT
59 : #include "methodjit/Retcon.h"
60 : #endif
61 :
62 : using namespace js;
63 :
64 : JSObject *
65 23355 : js_InitObjectClass(JSContext *cx, JSObject *obj)
66 : {
67 23355 : JS_ASSERT(obj->isNative());
68 :
69 23355 : return obj->asGlobal().getOrCreateObjectPrototype(cx);
70 : }
71 :
72 : JSObject *
73 10 : js_InitFunctionClass(JSContext *cx, JSObject *obj)
74 : {
75 10 : JS_ASSERT(obj->isNative());
76 :
77 10 : return obj->asGlobal().getOrCreateFunctionPrototype(cx);
78 : }
79 :
80 : static JSBool
81 0 : ThrowTypeError(JSContext *cx, unsigned argc, Value *vp)
82 : {
83 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
84 0 : JSMSG_THROW_TYPE_ERROR);
85 0 : return false;
86 : }
87 :
88 : namespace js {
89 :
90 : JSObject *
91 23629 : GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
92 : {
93 47258 : RootedVar<GlobalObject*> self(cx, this);
94 :
95 23629 : JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
96 23629 : JS_ASSERT(isNative());
97 :
98 : /*
99 : * Calling a function from a cleared global triggers this (yeah, I know).
100 : * Uncomment this once bug 470510 is fixed (if that bug doesn't remove
101 : * isCleared entirely).
102 : */
103 : // JS_ASSERT(!isCleared());
104 :
105 : /* If cx has no global object, make this the global object. */
106 23629 : if (!cx->globalObject)
107 18666 : JS_SetGlobalObject(cx, self);
108 :
109 47258 : RootedVarObject objectProto(cx);
110 :
111 : /*
112 : * Create |Object.prototype| first, mirroring CreateBlankProto but for the
113 : * prototype of the created object.
114 : */
115 23629 : objectProto = NewObjectWithGivenProto(cx, &ObjectClass, NULL, self);
116 23629 : if (!objectProto || !objectProto->setSingletonType(cx))
117 0 : return NULL;
118 :
119 : /*
120 : * The default 'new' type of Object.prototype is required by type inference
121 : * to have unknown properties, to simplify handling of e.g. heterogenous
122 : * objects in JSON and script literals.
123 : */
124 23629 : if (!objectProto->setNewTypeUnknown(cx))
125 0 : return NULL;
126 :
127 : /* Create |Function.prototype| next so we can create other functions. */
128 47258 : RootedVarFunction functionProto(cx);
129 : {
130 23629 : JSObject *functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self);
131 23629 : if (!functionProto_)
132 0 : return NULL;
133 23629 : functionProto = functionProto_->toFunction();
134 :
135 : /*
136 : * Bizarrely, |Function.prototype| must be an interpreted function, so
137 : * give it the guts to be one.
138 : */
139 : JSObject *proto = js_NewFunction(cx, functionProto,
140 23629 : NULL, 0, JSFUN_INTERPRETED, self, NULL);
141 23629 : if (!proto)
142 0 : return NULL;
143 23629 : JS_ASSERT(proto == functionProto);
144 23629 : functionProto->flags |= JSFUN_PROTOTYPE;
145 :
146 : JSScript *script =
147 23629 : JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
148 23629 : if (!script)
149 0 : return NULL;
150 23629 : script->noScriptRval = true;
151 23629 : script->code[0] = JSOP_STOP;
152 23629 : script->code[1] = SRC_NULL;
153 23629 : functionProto->initScript(script);
154 23629 : functionProto->getType(cx)->interpretedFunction = functionProto;
155 23629 : script->setFunction(functionProto);
156 :
157 23629 : if (!functionProto->setSingletonType(cx))
158 0 : return NULL;
159 :
160 : /*
161 : * The default 'new' type of Function.prototype is required by type
162 : * inference to have unknown properties, to simplify handling of e.g.
163 : * CloneFunctionObject.
164 : */
165 23629 : if (!functionProto->setNewTypeUnknown(cx))
166 0 : return NULL;
167 : }
168 :
169 : /* Create the Object function now that we have a [[Prototype]] for it. */
170 47258 : RootedVarFunction objectCtor(cx);
171 : {
172 23629 : JSObject *ctor = NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self);
173 23629 : if (!ctor)
174 0 : return NULL;
175 : objectCtor = js_NewFunction(cx, ctor, js_Object, 1, JSFUN_CONSTRUCTOR, self,
176 23629 : CLASS_ATOM(cx, Object));
177 23629 : if (!objectCtor)
178 0 : return NULL;
179 : }
180 :
181 : /*
182 : * Install |Object| and |Object.prototype| for the benefit of subsequent
183 : * code that looks for them.
184 : */
185 23629 : self->setObjectClassDetails(objectCtor, objectProto);
186 :
187 : /* Create |Function| so it and |Function.prototype| can be installed. */
188 47258 : RootedVarFunction functionCtor(cx);
189 : {
190 : JSObject *ctor =
191 23629 : NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self);
192 23629 : if (!ctor)
193 0 : return NULL;
194 : functionCtor = js_NewFunction(cx, ctor, Function, 1, JSFUN_CONSTRUCTOR, self,
195 23629 : CLASS_ATOM(cx, Function));
196 23629 : if (!functionCtor)
197 0 : return NULL;
198 23629 : JS_ASSERT(ctor == functionCtor);
199 : }
200 :
201 : /*
202 : * Install |Function| and |Function.prototype| so that we can freely create
203 : * functions and objects without special effort.
204 : */
205 23629 : self->setFunctionClassDetails(functionCtor, functionProto);
206 :
207 : /*
208 : * The hard part's done: now go back and add all the properties these
209 : * primordial values have.
210 : */
211 141774 : if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) ||
212 23629 : !DefinePropertiesAndBrand(cx, objectProto, object_props, object_methods) ||
213 23629 : !DefinePropertiesAndBrand(cx, objectCtor, NULL, object_static_methods) ||
214 23629 : !LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
215 23629 : !DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) ||
216 23629 : !DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL))
217 : {
218 0 : return NULL;
219 : }
220 :
221 : /* Add the global Function and Object properties now. */
222 23629 : jsid objectId = ATOM_TO_JSID(CLASS_ATOM(cx, Object));
223 23629 : if (!self->addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0))
224 0 : return NULL;
225 23629 : jsid functionId = ATOM_TO_JSID(CLASS_ATOM(cx, Function));
226 23629 : if (!self->addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0))
227 0 : return NULL;
228 :
229 : /* Heavy lifting done, but lingering tasks remain. */
230 :
231 : /* ES5 15.1.2.1. */
232 23629 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
233 23629 : JSObject *evalobj = js_DefineFunction(cx, self, id, eval, 1, JSFUN_STUB_GSOPS);
234 23629 : if (!evalobj)
235 0 : return NULL;
236 23629 : self->setOriginalEval(evalobj);
237 :
238 : /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
239 47258 : RootedVarFunction throwTypeError(cx);
240 23629 : throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, self, NULL);
241 23629 : if (!throwTypeError)
242 0 : return NULL;
243 47258 : AutoIdVector ids(cx);
244 23629 : if (!throwTypeError->preventExtensions(cx, &ids))
245 0 : return NULL;
246 23629 : self->setThrowTypeError(throwTypeError);
247 :
248 : /*
249 : * The global object should have |Object.prototype| as its [[Prototype]].
250 : * Eventually we'd like to have standard classes be there from the start,
251 : * and thus we would know we were always setting what had previously been a
252 : * null [[Prototype]], but right now some code assumes it can set the
253 : * [[Prototype]] before standard classes have been initialized. For now,
254 : * only set the [[Prototype]] if it hasn't already been set.
255 : */
256 23629 : if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, objectProto))
257 0 : return NULL;
258 :
259 : /*
260 : * Notify any debuggers about the creation of the script for
261 : * |Function.prototype| -- after all initialization, for simplicity.
262 : */
263 23629 : js_CallNewScriptHook(cx, functionProto->script(), functionProto);
264 23629 : return functionProto;
265 : }
266 :
267 : GlobalObject *
268 23659 : GlobalObject::create(JSContext *cx, Class *clasp)
269 : {
270 23659 : JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
271 :
272 47318 : RootedVar<GlobalObject*> obj(cx);
273 :
274 23659 : JSObject *obj_ = NewObjectWithGivenProto(cx, clasp, NULL, NULL);
275 23659 : if (!obj_)
276 0 : return NULL;
277 23659 : obj = &obj_->asGlobal();
278 :
279 23659 : if (!obj->setSingletonType(cx) || !obj->setVarObj(cx))
280 0 : return NULL;
281 :
282 : /* Construct a regexp statics object for this global object. */
283 23659 : JSObject *res = RegExpStatics::create(cx, obj);
284 23659 : if (!res)
285 0 : return NULL;
286 23659 : obj->initSlot(REGEXP_STATICS, ObjectValue(*res));
287 23659 : obj->initFlags(0);
288 :
289 23659 : return obj;
290 : }
291 :
292 : bool
293 264 : GlobalObject::initStandardClasses(JSContext *cx)
294 : {
295 264 : JSAtomState &state = cx->runtime->atomState;
296 :
297 : /* Define a top-level property 'undefined' with the undefined value. */
298 264 : if (!defineProperty(cx, state.typeAtoms[JSTYPE_VOID], UndefinedValue(),
299 264 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
300 : {
301 0 : return false;
302 : }
303 :
304 264 : if (!initFunctionAndObjectClasses(cx))
305 0 : return false;
306 :
307 : /* Initialize the rest of the standard objects and functions. */
308 264 : return js_InitArrayClass(cx, this) &&
309 264 : js_InitBooleanClass(cx, this) &&
310 264 : js_InitExceptionClasses(cx, this) &&
311 264 : js_InitMathClass(cx, this) &&
312 264 : js_InitNumberClass(cx, this) &&
313 264 : js_InitJSONClass(cx, this) &&
314 264 : js_InitRegExpClass(cx, this) &&
315 264 : js_InitStringClass(cx, this) &&
316 264 : js_InitTypedArrayClasses(cx, this) &&
317 : #if JS_HAS_XML_SUPPORT
318 264 : js_InitXMLClasses(cx, this) &&
319 : #endif
320 : #if JS_HAS_GENERATORS
321 264 : js_InitIteratorClasses(cx, this) &&
322 : #endif
323 264 : js_InitDateClass(cx, this) &&
324 264 : js_InitWeakMapClass(cx, this) &&
325 264 : js_InitProxyClass(cx, this) &&
326 264 : js_InitMapClass(cx, this) &&
327 3960 : js_InitSetClass(cx, this);
328 : }
329 :
330 : void
331 0 : GlobalObject::clear(JSContext *cx)
332 : {
333 0 : for (int key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
334 0 : setSlot(key, UndefinedValue());
335 :
336 : /* Clear regexp statics. */
337 0 : getRegExpStatics()->clear();
338 :
339 : /* Clear the runtime-codegen-enabled cache. */
340 0 : setSlot(RUNTIME_CODEGEN_ENABLED, UndefinedValue());
341 :
342 : /*
343 : * Clear the original-eval and [[ThrowTypeError]] slots, in case throwing
344 : * trying to execute a script for this global must reinitialize standard
345 : * classes. See bug 470150.
346 : */
347 0 : setSlot(EVAL, UndefinedValue());
348 0 : setSlot(THROWTYPEERROR, UndefinedValue());
349 :
350 : /*
351 : * Mark global as cleared. If we try to execute any compile-and-go
352 : * scripts from here on, we will throw.
353 : */
354 0 : int32_t flags = getSlot(FLAGS).toInt32();
355 0 : flags |= FLAGS_CLEARED;
356 0 : setSlot(FLAGS, Int32Value(flags));
357 :
358 : /*
359 : * Reset the new object cache in the compartment, which assumes that
360 : * prototypes cached on the global object are immutable.
361 : */
362 0 : cx->compartment->newObjectCache.reset();
363 :
364 : #ifdef JS_METHODJIT
365 : /*
366 : * Destroy compiled code for any scripts parented to this global. Call ICs
367 : * can directly call scripts which have associated JIT code, and do so
368 : * without checking whether the script's global has been cleared.
369 : */
370 0 : for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
371 0 : JSScript *script = i.get<JSScript>();
372 0 : if (script->compileAndGo && script->hasJITCode() && script->hasClearedGlobal()) {
373 0 : mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
374 0 : mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
375 : }
376 : }
377 : #endif
378 0 : }
379 :
380 : bool
381 108221 : GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx)
382 : {
383 108221 : HeapSlot &v = getSlotRef(RUNTIME_CODEGEN_ENABLED);
384 108221 : if (v.isUndefined()) {
385 : /*
386 : * If there are callbacks, make sure that the CSP callback is installed
387 : * and that it permits runtime code generation, then cache the result.
388 : */
389 5334 : JSCSPEvalChecker allows = cx->runtime->securityCallbacks->contentSecurityPolicyAllows;
390 5334 : v.set(this, RUNTIME_CODEGEN_ENABLED, BooleanValue(!allows || allows(cx)));
391 : }
392 108221 : return !v.isFalse();
393 : }
394 :
395 : JSFunction *
396 69713 : GlobalObject::createConstructor(JSContext *cx, Native ctor, JSAtom *name, unsigned length,
397 : gc::AllocKind kind)
398 : {
399 139426 : RootedVarObject self(cx, this);
400 69713 : return js_NewFunction(cx, NULL, ctor, length, JSFUN_CONSTRUCTOR, self, name, kind);
401 : }
402 :
403 : static JSObject *
404 71915 : CreateBlankProto(JSContext *cx, Class *clasp, JSObject &proto, GlobalObject &global)
405 : {
406 71915 : JS_ASSERT(clasp != &ObjectClass);
407 71915 : JS_ASSERT(clasp != &FunctionClass);
408 :
409 71915 : JSObject *blankProto = NewObjectWithGivenProto(cx, clasp, &proto, &global);
410 71915 : if (!blankProto || !blankProto->setSingletonType(cx))
411 0 : return NULL;
412 :
413 71915 : return blankProto;
414 : }
415 :
416 : JSObject *
417 44811 : GlobalObject::createBlankPrototype(JSContext *cx, Class *clasp)
418 : {
419 44811 : JSObject *objectProto = getOrCreateObjectPrototype(cx);
420 44811 : if (!objectProto)
421 0 : return NULL;
422 :
423 44811 : return CreateBlankProto(cx, clasp, *objectProto, *this);
424 : }
425 :
426 : JSObject *
427 27104 : GlobalObject::createBlankPrototypeInheriting(JSContext *cx, Class *clasp, JSObject &proto)
428 : {
429 27104 : return CreateBlankProto(cx, clasp, proto, *this);
430 : }
431 :
432 : bool
433 322273 : LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto)
434 : {
435 644546 : RootObject ctorRoot(cx, &ctor);
436 644546 : RootObject protoRoot(cx, &proto);
437 :
438 : return ctor->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom,
439 322273 : ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub,
440 322273 : JSPROP_PERMANENT | JSPROP_READONLY) &&
441 : proto->defineProperty(cx, cx->runtime->atomState.constructorAtom,
442 322273 : ObjectValue(*ctor), JS_PropertyStub, JS_StrictPropertyStub, 0);
443 : }
444 :
445 : bool
446 574608 : DefinePropertiesAndBrand(JSContext *cx, JSObject *obj, JSPropertySpec *ps, JSFunctionSpec *fs)
447 : {
448 1149216 : RootObject root(cx, &obj);
449 :
450 574608 : if ((ps && !JS_DefineProperties(cx, obj, ps)) || (fs && !JS_DefineFunctions(cx, obj, fs)))
451 0 : return false;
452 574608 : return true;
453 : }
454 :
455 : void
456 3332 : GlobalDebuggees_finalize(FreeOp *fop, JSObject *obj)
457 : {
458 3332 : fop->delete_((GlobalObject::DebuggerVector *) obj->getPrivate());
459 3332 : }
460 :
461 : static Class
462 : GlobalDebuggees_class = {
463 : "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
464 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
465 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, GlobalDebuggees_finalize
466 : };
467 :
468 : GlobalObject::DebuggerVector *
469 131913 : GlobalObject::getDebuggers()
470 : {
471 131913 : Value debuggers = getReservedSlot(DEBUGGERS);
472 131913 : if (debuggers.isUndefined())
473 4535 : return NULL;
474 127378 : JS_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class);
475 127378 : return (DebuggerVector *) debuggers.toObject().getPrivate();
476 : }
477 :
478 : GlobalObject::DebuggerVector *
479 4439 : GlobalObject::getOrCreateDebuggers(JSContext *cx)
480 : {
481 4439 : assertSameCompartment(cx, this);
482 4439 : DebuggerVector *debuggers = getDebuggers();
483 4439 : if (debuggers)
484 1107 : return debuggers;
485 :
486 3332 : JSObject *obj = NewObjectWithGivenProto(cx, &GlobalDebuggees_class, NULL, this);
487 3332 : if (!obj)
488 0 : return NULL;
489 3332 : debuggers = cx->new_<DebuggerVector>();
490 3332 : if (!debuggers)
491 0 : return NULL;
492 3332 : obj->setPrivate(debuggers);
493 3332 : setReservedSlot(DEBUGGERS, ObjectValue(*obj));
494 3332 : return debuggers;
495 : }
496 :
497 : bool
498 0 : GlobalObject::addDebugger(JSContext *cx, Debugger *dbg)
499 : {
500 0 : DebuggerVector *debuggers = getOrCreateDebuggers(cx);
501 0 : if (!debuggers)
502 0 : return false;
503 : #ifdef DEBUG
504 0 : for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++)
505 0 : JS_ASSERT(*p != dbg);
506 : #endif
507 0 : if (debuggers->empty() && !compartment()->addDebuggee(cx, this))
508 0 : return false;
509 0 : if (!debuggers->append(dbg)) {
510 0 : compartment()->removeDebuggee(cx->runtime->defaultFreeOp(), this);
511 0 : return false;
512 : }
513 0 : return true;
514 : }
515 :
516 : } // namespace js
|