PyV8.py 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import with_statement
  4. import sys, os
  5. try:
  6. from cStringIO import StringIO
  7. except ImportError:
  8. from StringIO import StringIO
  9. try:
  10. import json
  11. except ImportError:
  12. import simplejson as json
  13. import _PyV8
  14. __author__ = 'Flier Lu <flier.lu@gmail.com>'
  15. __version__ = '1.0'
  16. __all__ = ["JSError", "JSArray", "JSClass", "JSEngine", "JSContext", \
  17. "JSStackTrace", "JSStackFrame", "debugger", "profiler", \
  18. "JSExtension", "JSLocker", "JSUnlocker", "AST"]
  19. class JSError(Exception):
  20. def __init__(self, impl):
  21. Exception.__init__(self)
  22. self._impl = impl
  23. def __str__(self):
  24. return str(self._impl)
  25. def __unicode__(self):
  26. return unicode(self._impl)
  27. def __getattribute__(self, attr):
  28. impl = super(JSError, self).__getattribute__("_impl")
  29. try:
  30. return getattr(impl, attr)
  31. except AttributeError:
  32. return super(JSError, self).__getattribute__(attr)
  33. _PyV8._JSError._jsclass = JSError
  34. JSArray = _PyV8.JSArray
  35. JSExtension = _PyV8.JSExtension
  36. class JSLocker(_PyV8.JSLocker):
  37. def __enter__(self):
  38. self.enter()
  39. if JSContext.entered:
  40. self.leave()
  41. raise RuntimeError("Lock should be acquired before enter the context")
  42. return self
  43. def __exit__(self, exc_type, exc_value, traceback):
  44. if JSContext.entered:
  45. self.leave()
  46. raise RuntimeError("Lock should be released after leave the context")
  47. self.leave()
  48. def __nonzero__(self):
  49. return self.entered()
  50. class JSUnlocker(_PyV8.JSUnlocker):
  51. def __enter__(self):
  52. self.enter()
  53. return self
  54. def __exit__(self, exc_type, exc_value, traceback):
  55. self.leave()
  56. def __nonzero__(self):
  57. return self.entered()
  58. class JSClass(object):
  59. def __getattr__(self, name):
  60. if name == 'constructor':
  61. return JSClassConstructor(self.__class__)
  62. raise AttributeError(name)
  63. def toString(self):
  64. "Returns a string representation of an object."
  65. return "[object %s]" % self.__class__.__name__
  66. def toLocaleString(self):
  67. "Returns a value as a string value appropriate to the host environment's current locale."
  68. return self.toString()
  69. def valueOf(self):
  70. "Returns the primitive value of the specified object."
  71. return self
  72. def hasOwnProperty(self, name):
  73. "Returns a Boolean value indicating whether an object has a property with the specified name."
  74. return hasattr(self, name)
  75. def isPrototypeOf(self, obj):
  76. "Returns a Boolean value indicating whether an object exists in the prototype chain of another object."
  77. raise NotImplementedError()
  78. def __defineGetter__(self, name, getter):
  79. "Binds an object's property to a function to be called when that property is looked up."
  80. if hasattr(type(self), name):
  81. setter = getattr(type(self), name).fset
  82. else:
  83. setter = None
  84. setattr(type(self), name, property(fget=getter, fset=setter))
  85. def __lookupGetter__(self, name):
  86. "Return the function bound as a getter to the specified property."
  87. return self.name.fget
  88. def __defineSetter__(self, name, setter):
  89. "Binds an object's property to a function to be called when an attempt is made to set that property."
  90. if hasattr(type(self), name):
  91. getter = getattr(type(self), name).fget
  92. else:
  93. getter = None
  94. setattr(type(self), name, property(fget=getter, fset=setter))
  95. def __lookupSetter__(self, name):
  96. "Return the function bound as a setter to the specified property."
  97. return self.name.fset
  98. class JSClassConstructor(JSClass):
  99. def __init__(self, cls):
  100. self.cls = cls
  101. @property
  102. def name(self):
  103. return self.cls.__name__
  104. def toString(self):
  105. return "function %s() {\n [native code]\n}" % self.name
  106. def __call__(self, *args, **kwds):
  107. return self.cls(*args, **kwds)
  108. class JSDebug(object):
  109. class FrameData(object):
  110. def __init__(self, frame, count, name, value):
  111. self.frame = frame
  112. self.count = count
  113. self.name = name
  114. self.value = value
  115. def __len__(self):
  116. return self.count(self.frame)
  117. def __iter__(self):
  118. for i in xrange(self.count(self.frame)):
  119. yield (self.name(self.frame, i), self.value(self.frame, i))
  120. class Frame(object):
  121. def __init__(self, frame):
  122. self.frame = frame
  123. @property
  124. def index(self):
  125. return int(self.frame.index())
  126. @property
  127. def function(self):
  128. return self.frame.func()
  129. @property
  130. def receiver(self):
  131. return self.frame.receiver()
  132. @property
  133. def isConstructCall(self):
  134. return bool(self.frame.isConstructCall())
  135. @property
  136. def isDebuggerFrame(self):
  137. return bool(self.frame.isDebuggerFrame())
  138. @property
  139. def argumentCount(self):
  140. return int(self.frame.argumentCount())
  141. def argumentName(self, idx):
  142. return str(self.frame.argumentName(idx))
  143. def argumentValue(self, idx):
  144. return self.frame.argumentValue(idx)
  145. @property
  146. def arguments(self):
  147. return FrameData(self, self.argumentCount, self.argumentName, self.argumentValue)
  148. @property
  149. def localCount(self, idx):
  150. return int(self.frame.localCount())
  151. def localName(self, idx):
  152. return str(self.frame.localName(idx))
  153. def localValue(self, idx):
  154. return self.frame.localValue(idx)
  155. @property
  156. def locals(self):
  157. return FrameData(self, self.localCount, self.localName, self.localValue)
  158. @property
  159. def sourcePosition(self):
  160. return self.frame.sourcePosition()
  161. @property
  162. def sourceLine(self):
  163. return int(self.frame.sourceLine())
  164. @property
  165. def sourceColumn(self):
  166. return int(self.frame.sourceColumn())
  167. @property
  168. def sourceLineText(self):
  169. return str(self.frame.sourceLineText())
  170. def evaluate(self, source, disable_break = True):
  171. return self.frame.evaluate(source, disable_break)
  172. @property
  173. def invocationText(self):
  174. return str(self.frame.invocationText())
  175. @property
  176. def sourceAndPositionText(self):
  177. return str(self.frame.sourceAndPositionText())
  178. @property
  179. def localsText(self):
  180. return str(self.frame.localsText())
  181. def __str__(self):
  182. return str(self.frame.toText())
  183. class Frames(object):
  184. def __init__(self, state):
  185. self.state = state
  186. def __len__(self):
  187. return self.state.frameCount
  188. def __iter__(self):
  189. for i in xrange(self.state.frameCount):
  190. yield self.state.frame(i)
  191. class State(object):
  192. def __init__(self, state):
  193. self.state = state
  194. @property
  195. def frameCount(self):
  196. return int(self.state.frameCount())
  197. def frame(self, idx = None):
  198. return JSDebug.Frame(self.state.frame(idx))
  199. @property
  200. def selectedFrame(self):
  201. return int(self.state.selectedFrame())
  202. @property
  203. def frames(self):
  204. return JSDebug.Frames(self)
  205. def __repr__(self):
  206. s = StringIO()
  207. try:
  208. for frame in self.frames:
  209. s.write(str(frame))
  210. return s.getvalue()
  211. finally:
  212. s.close()
  213. class DebugEvent(object):
  214. pass
  215. class StateEvent(DebugEvent):
  216. __state = None
  217. @property
  218. def state(self):
  219. if not self.__state:
  220. self.__state = JSDebug.State(self.event.executionState())
  221. return self.__state
  222. class BreakEvent(StateEvent):
  223. type = _PyV8.JSDebugEvent.Break
  224. def __init__(self, event):
  225. self.event = event
  226. class ExceptionEvent(StateEvent):
  227. type = _PyV8.JSDebugEvent.Exception
  228. def __init__(self, event):
  229. self.event = event
  230. class NewFunctionEvent(DebugEvent):
  231. type = _PyV8.JSDebugEvent.NewFunction
  232. def __init__(self, event):
  233. self.event = event
  234. class Script(object):
  235. def __init__(self, script):
  236. self.script = script
  237. @property
  238. def source(self):
  239. return self.script.source()
  240. @property
  241. def id(self):
  242. return self.script.id()
  243. @property
  244. def name(self):
  245. return self.script.name()
  246. @property
  247. def lineOffset(self):
  248. return self.script.lineOffset()
  249. @property
  250. def lineCount(self):
  251. return self.script.lineCount()
  252. @property
  253. def columnOffset(self):
  254. return self.script.columnOffset()
  255. @property
  256. def type(self):
  257. return self.script.type()
  258. def __repr__(self):
  259. return "<%s script %s @ %d:%d> : '%s'" % (self.type, self.name,
  260. self.lineOffset, self.columnOffset,
  261. self.source)
  262. class CompileEvent(StateEvent):
  263. def __init__(self, event):
  264. self.event = event
  265. @property
  266. def script(self):
  267. if not hasattr(self, "_script"):
  268. setattr(self, "_script", JSDebug.Script(self.event.script()))
  269. return self._script
  270. def __str__(self):
  271. return str(self.script)
  272. class BeforeCompileEvent(CompileEvent):
  273. type = _PyV8.JSDebugEvent.BeforeCompile
  274. def __init__(self, event):
  275. JSDebug.CompileEvent.__init__(self, event)
  276. def __repr__(self):
  277. return "before compile script: %s\n%s" % (repr(self.script), repr(self.state))
  278. class AfterCompileEvent(CompileEvent):
  279. type = _PyV8.JSDebugEvent.AfterCompile
  280. def __init__(self, event):
  281. JSDebug.CompileEvent.__init__(self, event)
  282. def __repr__(self):
  283. return "after compile script: %s\n%s" % (repr(self.script), repr(self.state))
  284. onMessage = None
  285. onBreak = None
  286. onException = None
  287. onNewFunction = None
  288. onBeforeCompile = None
  289. onAfterCompile = None
  290. def __init__(self):
  291. self.seq = 0
  292. def nextSeq(self):
  293. seq = self.seq
  294. self.seq += 1
  295. return seq
  296. def isEnabled(self):
  297. return _PyV8.debug().enabled
  298. def setEnabled(self, enable):
  299. dbg = _PyV8.debug()
  300. if enable:
  301. dbg.onDebugEvent = self.onDebugEvent
  302. dbg.onDebugMessage = self.onDebugMessage
  303. dbg.onDispatchDebugMessages = self.onDispatchDebugMessages
  304. else:
  305. dbg.onDebugEvent = None
  306. dbg.onDebugMessage = None
  307. dbg.onDispatchDebugMessages = None
  308. dbg.enabled = enable
  309. enabled = property(isEnabled, setEnabled)
  310. def onDebugMessage(self, msg):
  311. if self.onMessage:
  312. self.onMessage(json.loads(msg))
  313. def onDebugEvent(self, type, evt):
  314. if type == _PyV8.JSDebugEvent.Break:
  315. if self.onBreak: self.onBreak(JSDebug.BreakEvent(evt))
  316. elif type == _PyV8.JSDebugEvent.Exception:
  317. if self.onException: self.onException(JSDebug.ExceptionEvent(evt))
  318. elif type == _PyV8.JSDebugEvent.NewFunction:
  319. if self.onNewFunction: self.onNewFunction(JSDebug.NewFunctionEvent(evt))
  320. elif type == _PyV8.JSDebugEvent.BeforeCompile:
  321. if self.onBeforeCompile: self.onBeforeCompile(JSDebug.BeforeCompileEvent(evt))
  322. elif type == _PyV8.JSDebugEvent.AfterCompile:
  323. if self.onAfterCompile: self.onAfterCompile(JSDebug.AfterCompileEvent(evt))
  324. def onDispatchDebugMessages(self):
  325. return True
  326. def breakForDebug(self):
  327. _PyV8.debug().debugBreak()
  328. def breakForCommand(self):
  329. _PyV8.debug().debugBreakForCommand()
  330. def sendCommand(self, cmd, *args, **kwds):
  331. request = json.dumps({
  332. 'seq': self.nextSeq(),
  333. 'type': 'request',
  334. 'command': cmd,
  335. 'arguments': kwds
  336. })
  337. _PyV8.debug().sendCommand(request)
  338. return request
  339. def debugContinue(self, action='next', steps=1):
  340. return self.sendCommand('continue', stepaction=action)
  341. def stepNext(self, steps=1):
  342. """Step to the next statement in the current function."""
  343. return self.debugContinue(action='next', steps=steps)
  344. def stepIn(self, steps=1):
  345. """Step into new functions invoked or the next statement in the current function."""
  346. return self.debugContinue(action='in', steps=steps)
  347. def stepOut(self, steps=1):
  348. """Step out of the current function."""
  349. return self.debugContinue(action='out', steps=steps)
  350. def stepMin(self, steps=1):
  351. """Perform a minimum step in the current function."""
  352. return self.debugContinue(action='out', steps=steps)
  353. debugger = JSDebug()
  354. class JSProfiler(_PyV8.JSProfiler):
  355. Modules = _PyV8.JSProfilerModules
  356. @property
  357. def logs(self):
  358. pos = 0
  359. while True:
  360. size, buf = self.getLogLines(pos)
  361. if size == 0:
  362. break
  363. for line in buf.split('\n'):
  364. yield line
  365. pos += size
  366. profiler = JSProfiler()
  367. class JSEngine(_PyV8.JSEngine):
  368. def __enter__(self):
  369. return self
  370. def __exit__(self, exc_type, exc_value, traceback):
  371. del self
  372. JSStackTrace = _PyV8.JSStackTrace
  373. JSStackTrace.Options = _PyV8.JSStackTraceOptions
  374. JSStackFrame = _PyV8.JSStackFrame
  375. class JSContext(_PyV8.JSContext):
  376. def __init__(self, obj=None, extensions=[]):
  377. if JSLocker.actived:
  378. self.lock = JSLocker()
  379. self.lock.enter()
  380. _PyV8.JSContext.__init__(self, obj, extensions)
  381. def __enter__(self):
  382. self.enter()
  383. return self
  384. def __exit__(self, exc_type, exc_value, traceback):
  385. self.leave()
  386. if hasattr(JSLocker, 'lock'):
  387. self.lock.leave()
  388. self.lock = None
  389. del self
  390. # contribute by marc boeker <http://code.google.com/u/marc.boeker/>
  391. def convert(obj):
  392. if type(obj) == _PyV8.JSArray:
  393. return [convert(v) for v in obj]
  394. if type(obj) == _PyV8.JSObject:
  395. return dict([[str(k), convert(obj.__getattr__(str(k)))] for k in obj.__members__])
  396. return obj
  397. class AST:
  398. Scope = _PyV8.AstScope
  399. Var = _PyV8.AstVariable
  400. Type = _PyV8.AstNodeType
  401. Node = _PyV8.AstNode
  402. Statement = _PyV8.AstStatement
  403. Expression = _PyV8.AstExpression
  404. Breakable = _PyV8.AstBreakableStatement
  405. Block = _PyV8.AstBlock
  406. Declaration = _PyV8.AstDeclaration
  407. Iteration = _PyV8.AstIterationStatement
  408. DoWhile = _PyV8.AstDoWhileStatement
  409. While = _PyV8.AstWhileStatement
  410. For = _PyV8.AstForStatement
  411. ForIn = _PyV8.AstForInStatement
  412. ExpressionStatement = _PyV8.AstExpressionStatement
  413. Continue = _PyV8.AstContinueStatement
  414. Break = _PyV8.AstBreakStatement
  415. Return = _PyV8.AstReturnStatement
  416. WithEnter = _PyV8.AstWithEnterStatement
  417. WithExit = _PyV8.AstWithExitStatement
  418. Case = _PyV8.AstCaseClause
  419. Switch = _PyV8.AstSwitchStatement
  420. Try = _PyV8.AstTryStatement
  421. TryCatch = _PyV8.AstTryCatchStatement
  422. TryFinally = _PyV8.AstTryFinallyStatement
  423. Debugger = _PyV8.AstDebuggerStatement
  424. Empty = _PyV8.AstEmptyStatement
  425. Literal = _PyV8.AstLiteral
  426. MaterializedLiteral = _PyV8.AstMaterializedLiteral
  427. Object = _PyV8.AstObjectLiteral
  428. RegExp = _PyV8.AstRegExpLiteral
  429. Array = _PyV8.AstArrayLiteral
  430. CatchExtension = _PyV8.AstCatchExtensionObject
  431. VarProxy = _PyV8.AstVariableProxy
  432. Slot = _PyV8.AstSlot
  433. Property = _PyV8.AstProperty
  434. Call = _PyV8.AstCall
  435. CallNew = _PyV8.AstCallNew
  436. CallRuntime = _PyV8.AstCallRuntime
  437. Op = _PyV8.AstOperation
  438. UnaryOp = _PyV8.AstUnaryOperation
  439. BinOp = _PyV8.AstBinaryOperation
  440. CountOp = _PyV8.AstCountOperation
  441. CompOp = _PyV8.AstCompareOperation
  442. Conditional = _PyV8.AstConditional
  443. Assignment = _PyV8.AstAssignment
  444. Throw = _PyV8.AstThrow
  445. Function = _PyV8.AstFunctionLiteral
  446. SharedFunction = _PyV8.AstSharedFunctionInfoLiteral
  447. This = _PyV8.AstThisFunction
  448. class PrettyPrint(object):
  449. def __init__(self):
  450. self.out = StringIO()
  451. def onFunctionLiteral(self, func):
  452. print >>self.out, "function ", func.name, "(",
  453. for i in range(func.scope.num_parameters):
  454. if i > 0: print ", ",
  455. print >>self.out, func.scope.parameter(i).name
  456. print >>self.out, ")"
  457. print >>self.out, "{"
  458. print >>self.out, "}"
  459. def __str__(self):
  460. return self.out.getvalue()
  461. import datetime
  462. import unittest
  463. import logging
  464. import traceback
  465. class TestContext(unittest.TestCase):
  466. def testMultiNamespace(self):
  467. self.assert_(not bool(JSContext.inContext))
  468. self.assert_(not bool(JSContext.entered))
  469. class Global(object):
  470. name = "global"
  471. g = Global()
  472. with JSContext(g) as ctxt:
  473. self.assert_(bool(JSContext.inContext))
  474. self.assertEquals(g.name, str(JSContext.entered.locals.name))
  475. self.assertEquals(g.name, str(JSContext.current.locals.name))
  476. class Local(object):
  477. name = "local"
  478. l = Local()
  479. with JSContext(l):
  480. self.assert_(bool(JSContext.inContext))
  481. self.assertEquals(l.name, str(JSContext.entered.locals.name))
  482. self.assertEquals(l.name, str(JSContext.current.locals.name))
  483. self.assert_(bool(JSContext.inContext))
  484. self.assertEquals(g.name, str(JSContext.entered.locals.name))
  485. self.assertEquals(g.name, str(JSContext.current.locals.name))
  486. self.assert_(not bool(JSContext.entered))
  487. self.assert_(not bool(JSContext.inContext))
  488. def _testMultiContext(self):
  489. # Create an environment
  490. with JSContext() as ctxt0:
  491. ctxt0.securityToken = "password"
  492. global0 = ctxt0.locals
  493. global0.custom = 1234
  494. self.assertEquals(1234, int(global0.custom))
  495. # Create an independent environment
  496. with JSContext() as ctxt1:
  497. ctxt1.securityToken = ctxt0.securityToken
  498. global1 = ctxt1.locals
  499. global1.custom = 1234
  500. self.assertEquals(1234, int(global0.custom))
  501. self.assertEquals(1234, int(global1.custom))
  502. # Now create a new context with the old global
  503. with JSContext(global1) as ctxt2:
  504. ctxt2.securityToken = ctxt1.securityToken
  505. self.assertRaises(AttributeError, int, global1.custom)
  506. self.assertRaises(AttributeError, int, global2.custom)
  507. def _testSecurityChecks(self):
  508. with JSContext() as env1:
  509. env1.securityToken = "foo"
  510. # Create a function in env1.
  511. env1.eval("spy=function(){return spy;}")
  512. spy = env1.locals.spy
  513. self.assert_(isinstance(spy, _PyV8.JSFunction))
  514. # Create another function accessing global objects.
  515. env1.eval("spy2=function(){return 123;}")
  516. spy2 = env1.locals.spy2
  517. self.assert_(isinstance(spy2, _PyV8.JSFunction))
  518. # Switch to env2 in the same domain and invoke spy on env2.
  519. env2 = JSContext()
  520. env2.securityToken = "foo"
  521. with env2:
  522. result = spy.apply(env2.locals)
  523. self.assert_(isinstance(result, _PyV8.JSFunction))
  524. env2.securityToken = "bar"
  525. # Call cross_domain_call, it should throw an exception
  526. with env2:
  527. self.assertRaises(JSError, spy2.apply, env2.locals)
  528. def _testCrossDomainDelete(self):
  529. with JSContext() as env1:
  530. env2 = JSContext()
  531. # Set to the same domain.
  532. env1.securityToken = "foo"
  533. env2.securityToken = "foo"
  534. env1.locals.prop = 3
  535. env2.locals.env1 = env1.locals
  536. # Change env2 to a different domain and delete env1.prop.
  537. #env2.securityToken = "bar"
  538. self.assertEquals(3, int(env1.eval("prop")))
  539. print env1.eval("env1")
  540. with env2:
  541. self.assertEquals(3, int(env2.eval("this.env1.prop")))
  542. self.assertEquals("false", str(e.eval("delete env1.prop")))
  543. # Check that env1.prop still exists.
  544. self.assertEquals(3, int(env1.locals.prop))
  545. class TestWrapper(unittest.TestCase):
  546. def testObject(self):
  547. with JSContext() as ctxt:
  548. o = ctxt.eval("new Object()")
  549. self.assert_(hash(o) > 0)
  550. o1 = o.clone()
  551. self.assertEquals(hash(o1), hash(o))
  552. self.assert_(o != o1)
  553. def testAutoConverter(self):
  554. with JSContext() as ctxt:
  555. ctxt.eval("""
  556. var_i = 1;
  557. var_f = 1.0;
  558. var_s = "test";
  559. var_b = true;
  560. """)
  561. vars = ctxt.locals
  562. var_i = vars.var_i
  563. self.assert_(var_i)
  564. self.assertEquals(1, int(var_i))
  565. var_f = vars.var_f
  566. self.assert_(var_f)
  567. self.assertEquals(1.0, float(vars.var_f))
  568. var_s = vars.var_s
  569. self.assert_(var_s)
  570. self.assertEquals("test", str(vars.var_s))
  571. var_b = vars.var_b
  572. self.assert_(var_b)
  573. self.assert_(bool(var_b))
  574. attrs = dir(ctxt.locals)
  575. self.assert_(attrs)
  576. self.assert_("var_i" in attrs)
  577. self.assert_("var_f" in attrs)
  578. self.assert_("var_s" in attrs)
  579. self.assert_("var_b" in attrs)
  580. def testExactConverter(self):
  581. class MyInteger(int, JSClass):
  582. pass
  583. class MyString(str, JSClass):
  584. pass
  585. class MyUnicode(unicode, JSClass):
  586. pass
  587. class MyDateTime(datetime.time, JSClass):
  588. pass
  589. class Global(JSClass):
  590. var_bool = True
  591. var_int = 1
  592. var_float = 1.0
  593. var_str = 'str'
  594. var_unicode = u'unicode'
  595. var_datetime = datetime.datetime.now()
  596. var_date = datetime.date.today()
  597. var_time = datetime.time()
  598. var_myint = MyInteger()
  599. var_mystr = MyString('mystr')
  600. var_myunicode = MyUnicode('myunicode')
  601. var_mytime = MyDateTime()
  602. with JSContext(Global()) as ctxt:
  603. typename = ctxt.eval("(function (name) { return this[name].constructor.name; })")
  604. typeof = ctxt.eval("(function (name) { return typeof(this[name]); })")
  605. self.assertEquals('Boolean', typename('var_bool'))
  606. self.assertEquals('Number', typename('var_int'))
  607. self.assertEquals('Number', typename('var_float'))
  608. self.assertEquals('String', typename('var_str'))
  609. self.assertEquals('String', typename('var_unicode'))
  610. self.assertEquals('Date', typename('var_datetime'))
  611. self.assertEquals('Date', typename('var_date'))
  612. self.assertEquals('Date', typename('var_time'))
  613. self.assertEquals('MyInteger', typename('var_myint'))
  614. self.assertEquals('MyString', typename('var_mystr'))
  615. self.assertEquals('MyUnicode', typename('var_myunicode'))
  616. self.assertEquals('MyDateTime', typename('var_mytime'))
  617. self.assertEquals('object', typeof('var_myint'))
  618. self.assertEquals('object', typeof('var_mystr'))
  619. self.assertEquals('object', typeof('var_myunicode'))
  620. self.assertEquals('object', typeof('var_mytime'))
  621. def testFunction(self):
  622. with JSContext() as ctxt:
  623. func = ctxt.eval("""
  624. (function ()
  625. {
  626. function a()
  627. {
  628. return "abc";
  629. }
  630. return a();
  631. })
  632. """)
  633. self.assertEquals("abc", str(func()))
  634. self.assert_(func != None)
  635. self.assertFalse(func == None)
  636. func = ctxt.eval("(function test() {})")
  637. self.assertEquals("test", func.name)
  638. #TODO fix me, why the setter doesn't work?
  639. func.name = "hello"
  640. #self.assertEquals("hello", func.name)
  641. def testCall(self):
  642. class Hello(object):
  643. def __call__(self, name):
  644. return "hello " + name
  645. class Global(JSClass):
  646. hello = Hello()
  647. with JSContext(Global()) as ctxt:
  648. self.assertEquals("hello flier", ctxt.eval("hello('flier')"))
  649. def testJSError(self):
  650. with JSContext() as ctxt:
  651. try:
  652. ctxt.eval('throw "test"')
  653. self.fail()
  654. except:
  655. self.assert_(JSError, sys.exc_type)
  656. def testErrorInfo(self):
  657. with JSContext() as ctxt:
  658. with JSEngine() as engine:
  659. try:
  660. engine.compile("""
  661. function hello()
  662. {
  663. throw Error("hello world");
  664. }
  665. hello();""", "test", 10, 10).run()
  666. self.fail()
  667. except JSError, e:
  668. self.assert_(str(e).startswith('JSError: Error: hello world ( test @ 14 : 34 ) ->'))
  669. self.assertEqual("Error", e.name)
  670. self.assertEqual("hello world", e.message)
  671. self.assertEqual("test", e.scriptName)
  672. self.assertEqual(14, e.lineNum)
  673. self.assertEqual(102, e.startPos)
  674. self.assertEqual(103, e.endPos)
  675. self.assertEqual(34, e.startCol)
  676. self.assertEqual(35, e.endCol)
  677. self.assertEqual('throw Error("hello world");', e.sourceLine.strip())
  678. self.assertEqual('Error: hello world\n' +
  679. ' at Error (unknown source)\n' +
  680. ' at hello (test:14:35)\n' +
  681. ' at test:17:25', e.stackTrace)
  682. def testStackTrace(self):
  683. class Global(JSClass):
  684. def GetCurrentStackTrace(self, limit):
  685. return JSStackTrace.GetCurrentStackTrace(4, JSStackTrace.Options.Detailed)
  686. with JSContext(Global()) as ctxt:
  687. st = ctxt.eval("""
  688. function a()
  689. {
  690. return GetCurrentStackTrace(10);
  691. }
  692. function b()
  693. {
  694. return eval("a()");
  695. }
  696. function c()
  697. {
  698. return new b();
  699. }
  700. c();""", "test")
  701. self.assertEquals(4, len(st))
  702. self.assertEquals("\tat a (test:4:28)\n\tat (eval)\n\tat b (test:8:28)\n\tat c (test:13:17)\n", str(st))
  703. self.assertEquals("test.a (4:28)\n. (1:1) eval\ntest.b (8:28) constructor\ntest.c (13:17)",
  704. "\n".join(["%s.%s (%d:%d)%s%s" % (
  705. f.scriptName, f.funcName, f.lineNum, f.column,
  706. ' eval' if f.isEval else '',
  707. ' constructor' if f.isConstructor else '') for f in st]))
  708. def testPythonException(self):
  709. class Global(JSClass):
  710. def raiseException(self):
  711. raise RuntimeError("Hello")
  712. with JSContext(Global()) as ctxt:
  713. r = ctxt.eval("""
  714. msg ="";
  715. try
  716. {
  717. this.raiseException()
  718. }
  719. catch(e)
  720. {
  721. msg += "catch " + e + ";";
  722. }
  723. finally
  724. {
  725. msg += "finally";
  726. }""")
  727. self.assertEqual("catch Error: Hello;finally", str(ctxt.locals.msg))
  728. def testExceptionMapping(self):
  729. class Global(JSClass):
  730. def raiseIndexError(self):
  731. return [1, 2, 3][5]
  732. def raiseAttributeError(self):
  733. None.hello()
  734. def raiseSyntaxError(self):
  735. eval("???")
  736. def raiseTypeError(self):
  737. int(sys)
  738. def raiseNotImplementedError(self):
  739. raise NotImplementedError("Not support")
  740. with JSContext(Global()) as ctxt:
  741. ctxt.eval("try { this.raiseIndexError(); } catch (e) { msg = e; }")
  742. self.assertEqual("RangeError: list index out of range", str(ctxt.locals.msg))
  743. ctxt.eval("try { this.raiseAttributeError(); } catch (e) { msg = e; }")
  744. self.assertEqual("ReferenceError: 'NoneType' object has no attribute 'hello'", str(ctxt.locals.msg))
  745. ctxt.eval("try { this.raiseSyntaxError(); } catch (e) { msg = e; }")
  746. self.assertEqual("SyntaxError: invalid syntax", str(ctxt.locals.msg))
  747. ctxt.eval("try { this.raiseTypeError(); } catch (e) { msg = e; }")
  748. self.assertEqual("TypeError: int() argument must be a string or a number, not 'module'", str(ctxt.locals.msg))
  749. ctxt.eval("try { this.raiseNotImplementedError(); } catch (e) { msg = e; }")
  750. self.assertEqual("Error: Not support", str(ctxt.locals.msg))
  751. def testArray(self):
  752. with JSContext() as ctxt:
  753. array = ctxt.eval("""
  754. var array = new Array();
  755. for (i=0; i<10; i++)
  756. {
  757. array[i] = 10-i;
  758. }
  759. array;
  760. """)
  761. self.assert_(isinstance(array, _PyV8.JSArray))
  762. self.assertEqual(10, len(array))
  763. self.assert_(5 in array)
  764. self.assertFalse(15 in array)
  765. l = list(array)
  766. self.assertEqual(10, len(l))
  767. for i in xrange(10):
  768. self.assertEqual(10-i, array[i])
  769. self.assertEqual(10-i, l[i])
  770. array[5] = 0
  771. self.assertEqual(0, array[5])
  772. del array[5]
  773. self.assertRaises(IndexError, lambda: array[5])
  774. ctxt.locals.array1 = JSArray(5)
  775. ctxt.locals.array2 = JSArray([1, 2, 3, 4, 5])
  776. for i in xrange(len(ctxt.locals.array2)):
  777. ctxt.locals.array1[i] = ctxt.locals.array2[i] * 10
  778. ctxt.eval("""
  779. var sum = 0;
  780. for (i=0; i<array1.length; i++)
  781. sum += array1[i]
  782. for (i=0; i<array2.length; i++)
  783. sum += array2[i]
  784. """)
  785. self.assertEqual(165, ctxt.locals.sum)
  786. ctxt.locals.array3 = [1, 2, 3, 4, 5]
  787. self.assert_(ctxt.eval('array3[1] === 2'))
  788. self.assert_(ctxt.eval('array3[9] === undefined'))
  789. def testMultiDimArray(self):
  790. with JSContext() as ctxt:
  791. ret = ctxt.eval("""
  792. ({
  793. 'test': function(){
  794. return [
  795. [ 1, 'abla' ],
  796. [ 2, 'ajkss' ],
  797. ]
  798. }
  799. })
  800. """).test()
  801. self.assertEquals([[1, 'abla'], [2, 'ajkss']], convert(ret))
  802. def testLazyConstructor(self):
  803. class Globals(JSClass):
  804. def __init__(self):
  805. self.array=JSArray([1,2,3])
  806. with JSContext(Globals()) as ctxt:
  807. self.assertEqual(2, ctxt.eval("""array[1]"""))
  808. def testForEach(self):
  809. class NamedClass(JSClass):
  810. foo = 1
  811. def __init__(self):
  812. self.bar = 2
  813. def gen(x):
  814. yield 0
  815. yield 1
  816. yield 2
  817. with JSContext() as ctxt:
  818. func = ctxt.eval("""(function (k) {
  819. var result = [];
  820. for (var prop in k) {
  821. result.push(prop);
  822. }
  823. return result;
  824. })""")
  825. self.assertEquals(["bar"], list(func(NamedClass())))
  826. self.assertEquals(["0", "1", "2"], list(func([1, 2, 3])))
  827. self.assertEquals(["1", "2", "3"], list(func({1:1, 2:2, 3:3})))
  828. self.assertEquals(["0", "1", "2"], list(func(gen(3))))
  829. def testDict(self):
  830. import UserDict
  831. with JSContext() as ctxt:
  832. obj = ctxt.eval("var r = { 'a' : 1, 'b' : 2 }; r")
  833. self.assertEqual(1, obj.a)
  834. self.assertEqual(2, obj.b)
  835. self.assertEqual({ 'a' : 1, 'b' : 2 }, dict(obj))
  836. self.assertEqual({ 'a': 1,
  837. 'b': [1, 2, 3],
  838. 'c': { 'str' : 'goofy',
  839. 'float' : 1.234,
  840. 'obj' : { 'name': 'john doe' }},
  841. 'd': True,
  842. 'e': None },
  843. convert(ctxt.eval("""var x =
  844. { a: 1,
  845. b: [1, 2, 3],
  846. c: { str: 'goofy',
  847. float: 1.234,
  848. obj: { name: 'john doe' }},
  849. d: true,
  850. e: null }; x""")))
  851. def testDate(self):
  852. with JSContext() as ctxt:
  853. now1 = ctxt.eval("new Date();")
  854. self.assert_(now1)
  855. now2 = datetime.datetime.utcnow()
  856. delta = now2 - now1 if now2 > now1 else now1 - now2
  857. self.assert_(delta < datetime.timedelta(seconds=1))
  858. func = ctxt.eval("(function (d) { return d.toString(); })")
  859. now = datetime.datetime.now()
  860. self.assert_(str(func(now)).startswith(now.strftime("%a %b %d %Y %H:%M:%S")))
  861. def testUnicode(self):
  862. with JSContext() as ctxt:
  863. self.assertEquals(u"人", unicode(ctxt.eval("\"人\""), "utf-8"))
  864. self.assertEquals(u"é", unicode(ctxt.eval("\"é\""), "utf-8"))
  865. func = ctxt.eval("(function (msg) { return msg.length; })")
  866. self.assertEquals(2, func(u"测试"))
  867. def testClassicStyleObject(self):
  868. class FileSystemWarpper:
  869. @property
  870. def cwd(self):
  871. return os.getcwd()
  872. class Global:
  873. @property
  874. def fs(self):
  875. return FileSystemWarpper()
  876. with JSContext(Global()) as ctxt:
  877. self.assertEquals(os.getcwd(), ctxt.eval("fs.cwd"))
  878. def testRefCount(self):
  879. count = sys.getrefcount(None)
  880. class Global(JSClass):
  881. pass
  882. with JSContext(Global()) as ctxt:
  883. ctxt.eval("""
  884. var none = null;
  885. """)
  886. self.assertEquals(count+1, sys.getrefcount(None))
  887. ctxt.eval("""
  888. var none = null;
  889. """)
  890. self.assertEquals(count+1, sys.getrefcount(None))
  891. def testProperty(self):
  892. class Global(JSClass):
  893. def __init__(self, name):
  894. self._name = name
  895. def getname(self):
  896. return self._name
  897. def setname(self, name):
  898. self._name = name
  899. def delname(self):
  900. self._name = 'deleted'
  901. name = property(getname, setname, delname)
  902. with JSContext(Global('world')) as ctxt:
  903. self.assertEquals('world', ctxt.eval("name"))
  904. self.assertEquals('flier', ctxt.eval("name = 'flier';"))
  905. self.assertEquals('flier', ctxt.eval("name"))
  906. self.assert_(ctxt.eval("delete name")) # FIXME
  907. #self.assertEquals('deleted', ctxt.eval("name"))
  908. ctxt.eval("__defineGetter__('name', function() { return 'fixed'; });")
  909. self.assertEquals('fixed', ctxt.eval("name"))
  910. def _testDestructor(self):
  911. import gc
  912. owner = self
  913. owner.deleted = False
  914. class Hello(object):
  915. def say(self):
  916. pass
  917. def __del__(self):
  918. owner.deleted = True
  919. def test():
  920. with JSContext() as ctxt:
  921. fn = ctxt.eval("(function (obj) { obj.say(); })")
  922. obj = Hello()
  923. self.assert_(2, sys.getrefcount(obj))
  924. fn(obj)
  925. self.assert_(3, sys.getrefcount(obj))
  926. del obj
  927. test()
  928. self.assertFalse(owner.deleted)
  929. JSEngine.collect()
  930. gc.collect()
  931. self.assert_(self.deleted)
  932. def testNullInString(self):
  933. with JSContext() as ctxt:
  934. fn = ctxt.eval("(function (s) { return s; })")
  935. self.assertEquals("hello \0 world", fn("hello \0 world"))
  936. class TestMultithread(unittest.TestCase):
  937. def testLocker(self):
  938. self.assertFalse(JSLocker.actived)
  939. self.assertFalse(JSLocker.locked)
  940. with JSLocker() as outter_locker:
  941. self.assertTrue(JSLocker.actived)
  942. self.assertTrue(JSLocker.locked)
  943. self.assertTrue(outter_locker)
  944. with JSLocker() as inner_locker:
  945. self.assertTrue(JSLocker.locked)
  946. self.assertTrue(outter_locker)
  947. self.assertTrue(inner_locker)
  948. with JSUnlocker() as unlocker:
  949. self.assertFalse(JSLocker.locked)
  950. self.assertTrue(outter_locker)
  951. self.assertTrue(inner_locker)
  952. self.assertTrue(JSLocker.locked)
  953. self.assertTrue(JSLocker.actived)
  954. self.assertFalse(JSLocker.locked)
  955. locker = JSLocker()
  956. with JSContext():
  957. self.assertRaises(RuntimeError, locker.__enter__)
  958. self.assertRaises(RuntimeError, locker.__exit__, None, None, None)
  959. del locker
  960. def testMultiPythonThread(self):
  961. import time, threading
  962. class Global:
  963. count = 0
  964. started = threading.Event()
  965. finished = threading.Semaphore(0)
  966. def sleep(self, ms):
  967. time.sleep(ms / 1000.0)
  968. self.count += 1
  969. g = Global()
  970. def run():
  971. with JSContext(g) as ctxt:
  972. ctxt.eval("""
  973. started.wait();
  974. for (i=0; i<10; i++)
  975. {
  976. sleep(100);
  977. }
  978. finished.release();
  979. """)
  980. threading.Thread(target=run).start()
  981. now = time.time()
  982. self.assertEqual(0, g.count)
  983. g.started.set()
  984. g.finished.acquire()
  985. self.assertEqual(10, g.count)
  986. self.assert_((time.time() - now) >= 1)
  987. def testMultiJavascriptThread(self):
  988. import time, thread, threading
  989. class Global:
  990. result = []
  991. def add(self, value):
  992. with JSUnlocker() as unlocker:
  993. time.sleep(0.1)
  994. self.result.append(value)
  995. g = Global()
  996. def run():
  997. with JSContext(g) as ctxt:
  998. ctxt.eval("""
  999. for (i=0; i<10; i++)
  1000. add(i);
  1001. """)
  1002. threads = [threading.Thread(target=run), threading.Thread(target=run)]
  1003. with JSLocker():
  1004. for t in threads: t.start()
  1005. for t in threads: t.join()
  1006. self.assertEqual(20, len(g.result))
  1007. def _testPreemptionJavascriptThreads(self):
  1008. import time, thread, threading
  1009. class Global:
  1010. result = []
  1011. def add(self, value):
  1012. # we use preemption scheduler to switch between threads
  1013. # so, just comment the JSUnlocker
  1014. #
  1015. # with JSUnlocker() as unlocker:
  1016. time.sleep(0.1)
  1017. self.result.append(value)
  1018. g = Global()
  1019. def run():
  1020. with JSContext(g) as ctxt:
  1021. ctxt.eval("""
  1022. for (i=0; i<10; i++)
  1023. add(i);
  1024. """)
  1025. threads = [threading.Thread(target=run), threading.Thread(target=run)]
  1026. with JSLocker() as locker:
  1027. JSLocker.startPreemption(100)
  1028. for t in threads: t.start()
  1029. for t in threads: t.join()
  1030. self.assertEqual(20, len(g.result))
  1031. class TestEngine(unittest.TestCase):
  1032. def testClassProperties(self):
  1033. with JSContext() as ctxt:
  1034. self.assert_(str(JSEngine.version).startswith("3."))
  1035. self.assertFalse(JSEngine.dead)
  1036. def testCompile(self):
  1037. with JSContext() as ctxt:
  1038. with JSEngine() as engine:
  1039. s = engine.compile("1+2")
  1040. self.assert_(isinstance(s, _PyV8.JSScript))
  1041. self.assertEquals("1+2", s.source)
  1042. self.assertEquals(3, int(s.run()))
  1043. def testPrecompile(self):
  1044. with JSContext() as ctxt:
  1045. with JSEngine() as engine:
  1046. data = engine.precompile("1+2")
  1047. self.assert_(data)
  1048. self.assertEquals(28, len(data))
  1049. s = engine.compile("1+2", precompiled=data)
  1050. self.assert_(isinstance(s, _PyV8.JSScript))
  1051. self.assertEquals("1+2", s.source)
  1052. self.assertEquals(3, int(s.run()))
  1053. def testExtension(self):
  1054. extSrc = """function hello(name) { return "hello " + name + " from javascript"; }"""
  1055. extJs = JSExtension("hello/javascript", extSrc)
  1056. self.assert_(extJs)
  1057. self.assertEqual("hello/javascript", extJs.name)
  1058. self.assertEqual(extSrc, extJs.source)
  1059. self.assertFalse(extJs.autoEnable)
  1060. self.assertTrue(extJs.registered)
  1061. TestEngine.extJs = extJs
  1062. with JSContext(extensions=['hello/javascript']) as ctxt:
  1063. self.assertEqual("hello flier from javascript", ctxt.eval("hello('flier')"))
  1064. # test the auto enable property
  1065. with JSContext() as ctxt:
  1066. self.assertRaises(JSError, ctxt.eval, "hello('flier')")
  1067. extJs.autoEnable = True
  1068. self.assertTrue(extJs.autoEnable)
  1069. with JSContext() as ctxt:
  1070. self.assertEqual("hello flier from javascript", ctxt.eval("hello('flier')"))
  1071. extJs.autoEnable = False
  1072. self.assertFalse(extJs.autoEnable)
  1073. with JSContext() as ctxt:
  1074. self.assertRaises(JSError, ctxt.eval, "hello('flier')")
  1075. def testNativeExtension(self):
  1076. extSrc = "native function hello();"
  1077. extPy = JSExtension("hello/python", extSrc, lambda func: lambda name: "hello " + name + " from python", register=False)
  1078. self.assert_(extPy)
  1079. self.assertEqual("hello/python", extPy.name)
  1080. self.assertEqual(extSrc, extPy.source)
  1081. self.assertFalse(extPy.autoEnable)
  1082. self.assertFalse(extPy.registered)
  1083. extPy.register()
  1084. self.assertTrue(extPy.registered)
  1085. TestEngine.extPy = extPy
  1086. with JSContext(extensions=['hello/python']) as ctxt:
  1087. self.assertEqual("hello flier from python", ctxt.eval("hello('flier')"))
  1088. def _testSerialize(self):
  1089. data = None
  1090. self.assertFalse(JSContext.entered)
  1091. with JSContext() as ctxt:
  1092. self.assert_(JSContext.entered)
  1093. #ctxt.eval("function hello(name) { return 'hello ' + name; }")
  1094. data = JSEngine.serialize()
  1095. self.assert_(data)
  1096. self.assert_(len(data) > 0)
  1097. self.assertFalse(JSContext.entered)
  1098. #JSEngine.deserialize()
  1099. self.assert_(JSContext.entered)
  1100. self.assertEquals('hello flier', JSContext.current.eval("hello('flier');"))
  1101. def testEval(self):
  1102. with JSContext() as ctxt:
  1103. self.assertEquals(3, int(ctxt.eval("1+2")))
  1104. def testGlobal(self):
  1105. class Global(JSClass):
  1106. version = "1.0"
  1107. with JSContext(Global()) as ctxt:
  1108. vars = ctxt.locals
  1109. # getter
  1110. self.assertEquals(Global.version, str(vars.version))
  1111. self.assertEquals(Global.version, str(ctxt.eval("version")))
  1112. self.assertEquals(None, ctxt.eval("nonexists"))
  1113. # setter
  1114. self.assertEquals(2.0, float(ctxt.eval("version = 2.0")))
  1115. self.assertEquals(2.0, float(vars.version))
  1116. def testThis(self):
  1117. class Global(JSClass):
  1118. version = 1.0
  1119. with JSContext(Global()) as ctxt:
  1120. self.assertEquals("[object Global]", str(ctxt.eval("this")))
  1121. self.assertEquals(1.0, float(ctxt.eval("this.version")))
  1122. def testObjectBuildInMethods(self):
  1123. class Global(JSClass):
  1124. version = 1.0
  1125. with JSContext(Global()) as ctxt:
  1126. self.assertEquals("[object Global]", str(ctxt.eval("this.toString()")))
  1127. self.assertEquals("[object Global]", str(ctxt.eval("this.toLocaleString()")))
  1128. self.assertEquals(Global.version, float(ctxt.eval("this.valueOf()").version))
  1129. self.assert_(bool(ctxt.eval("this.hasOwnProperty(\"version\")")))
  1130. self.assertFalse(ctxt.eval("this.hasOwnProperty(\"nonexistent\")"))
  1131. def testPythonWrapper(self):
  1132. class Global(JSClass):
  1133. s = [1, 2, 3]
  1134. d = {'a': {'b': 'c'}, 'd': ['e', 'f']}
  1135. g = Global()
  1136. with JSContext(g) as ctxt:
  1137. ctxt.eval("""
  1138. s[2] = s[1] + 2;
  1139. s[0] = s[1];
  1140. delete s[1];
  1141. """)
  1142. self.assertEquals([2, 4], g.s)
  1143. self.assertEquals('c', ctxt.eval("d.a.b"))
  1144. self.assertEquals(['e', 'f'], ctxt.eval("d.d"))
  1145. ctxt.eval("""
  1146. d.a.q = 4
  1147. delete d.d
  1148. """)
  1149. self.assertEquals(4, g.d['a']['q'])
  1150. self.assertEquals(None, ctxt.eval("d.d"))
  1151. class TestDebug(unittest.TestCase):
  1152. def setUp(self):
  1153. self.engine = JSEngine()
  1154. def tearDown(self):
  1155. del self.engine
  1156. events = []
  1157. def processDebugEvent(self, event):
  1158. try:
  1159. logging.debug("receive debug event: %s", repr(event))
  1160. self.events.append(repr(event))
  1161. except:
  1162. logging.error("fail to process debug event")
  1163. logging.debug(traceback.extract_stack())
  1164. def testEventDispatch(self):
  1165. global debugger
  1166. self.assert_(not debugger.enabled)
  1167. debugger.onBreak = lambda evt: self.processDebugEvent(evt)
  1168. debugger.onException = lambda evt: self.processDebugEvent(evt)
  1169. debugger.onNewFunction = lambda evt: self.processDebugEvent(evt)
  1170. debugger.onBeforeCompile = lambda evt: self.processDebugEvent(evt)
  1171. debugger.onAfterCompile = lambda evt: self.processDebugEvent(evt)
  1172. with JSContext() as ctxt:
  1173. debugger.enabled = True
  1174. self.assertEquals(3, int(ctxt.eval("function test() { text = \"1+2\"; return eval(text) } test()")))
  1175. debugger.enabled = False
  1176. self.assertRaises(JSError, JSContext.eval, ctxt, "throw 1")
  1177. self.assert_(not debugger.enabled)
  1178. self.assertEquals(4, len(self.events))
  1179. class TestProfile(unittest.TestCase):
  1180. def _testStart(self):
  1181. self.assertFalse(profiler.started)
  1182. profiler.start()
  1183. self.assert_(profiler.started)
  1184. profiler.stop()
  1185. self.assertFalse(profiler.started)
  1186. def _testResume(self):
  1187. self.assert_(profiler.paused)
  1188. self.assertEquals(profiler.Modules.cpu, profiler.modules)
  1189. profiler.resume()
  1190. profiler.resume(profiler.Modules.heap)
  1191. # TODO enable profiler with resume
  1192. #self.assertFalse(profiler.paused)
  1193. class TestAST(unittest.TestCase):
  1194. class Checker(object):
  1195. def __init__(self, testcase):
  1196. self.testcase = testcase
  1197. self.called = 0
  1198. def __getattr__(self, name):
  1199. return getattr(self.testcase, name)
  1200. def test(self, script):
  1201. with JSContext() as ctxt:
  1202. JSEngine().compile(script).visit(self)
  1203. return self.called
  1204. def testBlock(self):
  1205. class BlockChecker(TestAST.Checker):
  1206. def onBlock(self, stmt):
  1207. self.called += 1
  1208. self.assertEquals(AST.Type.Block, stmt.type)
  1209. self.assert_(stmt.initializerBlock)
  1210. self.assertFalse(stmt.anonymous)
  1211. target = stmt.breakTarget
  1212. self.assert_(target)
  1213. self.assertFalse(target.bound)
  1214. self.assert_(target.unused)
  1215. self.assertFalse(target.linked)
  1216. label = stmt.breakTarget.entryLabel
  1217. self.assert_(label)
  1218. self.assertFalse(label.bound)
  1219. self.assert_(label.unused)
  1220. self.assertFalse(label.linked)
  1221. self.assertEquals(2, len(stmt.statements))
  1222. self.assertEquals(['%InitializeVarGlobal("i");', '%InitializeVarGlobal("j");'], [str(s) for s in stmt.statements])
  1223. self.assertEquals(1, BlockChecker(self).test("var i, j;"))
  1224. def testIfStatement(self):
  1225. class IfStatementChecker(TestAST.Checker):
  1226. def onIfStatement(self, stmt):
  1227. self.called += 1
  1228. self.assert_(stmt)
  1229. self.assertEquals(AST.Type.IfStatement, stmt.type)
  1230. self.assertEquals(7, stmt.pos)
  1231. stmt.pos = 100
  1232. self.assertEquals(100, stmt.pos)
  1233. self.assert_(stmt.hasThenStatement)
  1234. self.assert_(stmt.hasElseStatement)
  1235. self.assertEquals("((value%2)==0)", str(stmt.condition))
  1236. self.assertEquals("{ s = \"even\"; }", str(stmt.thenStatement))
  1237. self.assertEquals("{ s = \"odd\"; }", str(stmt.elseStatement))
  1238. self.assertFalse(stmt.condition.trivial)
  1239. self.assertFalse(stmt.condition.propertyName)
  1240. self.assertFalse(stmt.condition.loopCondition)
  1241. self.assertEquals(1, IfStatementChecker(self).test("var s; if (value % 2 == 0) { s = 'even'; } else { s = 'odd'; }"))
  1242. def testForStatement(self):
  1243. class ForStatementChecker(TestAST.Checker):
  1244. def onForStatement(self, stmt):
  1245. self.called += 1
  1246. self.assertEquals("{ j += i; }", str(stmt.body))
  1247. self.assertEquals("i = 0;", str(stmt.init))
  1248. self.assertEquals("(i<10)", str(stmt.condition))
  1249. self.assertEquals("(i++);", str(stmt.next))
  1250. target = stmt.continueTarget
  1251. self.assert_(target)
  1252. self.assertFalse(target.bound)
  1253. self.assert_(target.unused)
  1254. self.assertFalse(target.linked)
  1255. self.assertFalse(stmt.fastLoop)
  1256. self.assertEquals(1, ForStatementChecker(self).test("var j; for (i=0; i<10; i++) { j+=i; }"))
  1257. def testForInStatement(self):
  1258. class ForInStatementChecker(TestAST.Checker):
  1259. def onForInStatement(self, stmt):
  1260. self.called += 1
  1261. self.assertEquals("{ out += name; }", str(stmt.body))
  1262. self.assertEquals("name", str(stmt.each))
  1263. self.assertEquals("names", str(stmt.enumerable))
  1264. self.assertEquals(1, ForInStatementChecker(self).test("var names = new Array(); var out = ''; for (name in names) { out += name; }"))
  1265. def testWhileStatement(self):
  1266. class WhileStatementChecker(TestAST.Checker):
  1267. def onWhileStatement(self, stmt):
  1268. self.called += 1
  1269. self.assertEquals("{ i += 1; }", str(stmt.body))
  1270. self.assertEquals("(i<10)", str(stmt.condition))
  1271. self.assertEquals(1, WhileStatementChecker(self).test("var i; while (i<10) { i += 1; }"))
  1272. def testDoWhileStatement(self):
  1273. class DoWhileStatementChecker(TestAST.Checker):
  1274. def onDoWhileStatement(self, stmt):
  1275. self.called += 1
  1276. self.assertEquals("{ i += 1; }", str(stmt.body))
  1277. self.assertEquals("(i<10)", str(stmt.condition))
  1278. self.assertEquals(28, stmt.conditionPos)
  1279. self.assertEquals(1, DoWhileStatementChecker(self).test("var i; do { i += 1; } while (i<10);"))
  1280. def testCallStatements(self):
  1281. class CallStatementChecker(TestAST.Checker):
  1282. def onBlock(self, block):
  1283. for stmt in block.statements:
  1284. stmt.visit(self)
  1285. def onExpressionStatement(self, stmt):
  1286. stmt.expression.visit(self)
  1287. def onCall(self, expr):
  1288. self.called += 1
  1289. self.assertEquals("hello", str(expr.expression))
  1290. self.assertEquals(['"flier"'], [str(arg) for arg in expr.args])
  1291. self.assertEquals(143, expr.pos)
  1292. def onCallNew(self, expr):
  1293. self.called += 1
  1294. self.assertEquals("dog", str(expr.expression))
  1295. self.assertEquals(['"cat"'], [str(arg) for arg in expr.args])
  1296. self.assertEquals(171, expr.pos)
  1297. def onCallRuntime(self, expr):
  1298. self.called += 1
  1299. self.assertEquals("InitializeVarGlobal", expr.name)
  1300. self.assertEquals(['"s"'], [str(arg) for arg in expr.args])
  1301. self.assertFalse(expr.isJsRuntime)
  1302. self.assertEquals(3, CallStatementChecker(self).test("""
  1303. var s;
  1304. function hello(name) { s = "Hello " + name; }
  1305. function dog(name) { this.name = name; }
  1306. hello("flier");
  1307. new dog("cat");
  1308. """))
  1309. def testTryStatements(self):
  1310. class TryStatementsChecker(TestAST.Checker):
  1311. def onBlock(self, block):
  1312. for stmt in block.statements:
  1313. stmt.visit(self)
  1314. def onExpressionStatement(self, stmt):
  1315. stmt.expression.visit(self)
  1316. def onThrow(self, expr):
  1317. self.called += 1
  1318. self.assertEquals('"abc"', str(expr.exception))
  1319. self.assertEquals(54, expr.pos)
  1320. def onTryCatchStatement(self, stmt):
  1321. self.called += 1
  1322. self.assertEquals("{ throw \"abc\"; }", str(stmt.tryBlock))
  1323. self.assertEquals([], stmt.targets)
  1324. stmt.tryBlock.visit(self)
  1325. self.assertEquals(".catch-var", str(stmt.catchVar))
  1326. self.assertEquals("{ <enter with> ({ \"err\": .catch-var }) try { { s = err; } } finally { <exit with> } }", str(stmt.catchBlock))
  1327. def onTryFinallyStatement(self, stmt):
  1328. self.called += 1
  1329. self.assertEquals("{ throw \"abc\"; }", str(stmt.tryBlock))
  1330. self.assertEquals([], stmt.targets)
  1331. self.assertEquals("{ s += \".\"; }", str(stmt.finallyBlock))
  1332. self.assertEquals(3, TryStatementsChecker(self).test("""
  1333. var s;
  1334. try {
  1335. throw "abc";
  1336. }
  1337. catch (err) {
  1338. s = err;
  1339. };
  1340. try {
  1341. throw "abc";
  1342. }
  1343. finally {
  1344. s += ".";
  1345. }
  1346. """))
  1347. def testPrettyPrint(self):
  1348. pp = PrettyPrint()
  1349. with JSContext() as ctxt:
  1350. script = JSEngine().compile("function hello(name) { return 'hello ' + name; }")
  1351. script.visit(pp)
  1352. self.assertEquals("", str(pp))
  1353. if __name__ == '__main__':
  1354. if "-v" in sys.argv:
  1355. level = logging.DEBUG
  1356. else:
  1357. level = logging.WARN
  1358. if "-p" in sys.argv:
  1359. sys.argv.remove("-p")
  1360. print "Press any key to continue..."
  1361. raw_input()
  1362. logging.basicConfig(level=level, format='%(asctime)s %(levelname)s %(message)s')
  1363. logging.info("testing PyV8 module %s with V8 v%s", __version__, JSEngine.version)
  1364. unittest.main()