123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- from __future__ import with_statement
- import sys, os
- try:
- from cStringIO import StringIO
- except ImportError:
- from StringIO import StringIO
- try:
- import json
- except ImportError:
- import simplejson as json
- import _PyV8
- __author__ = 'Flier Lu <flier.lu@gmail.com>'
- __version__ = '1.0'
- __all__ = ["JSError", "JSArray", "JSClass", "JSEngine", "JSContext", \
- "JSStackTrace", "JSStackFrame", "debugger", "profiler", \
- "JSExtension", "JSLocker", "JSUnlocker", "AST"]
- class JSError(Exception):
- def __init__(self, impl):
- Exception.__init__(self)
- self._impl = impl
- def __str__(self):
- return str(self._impl)
- def __unicode__(self):
- return unicode(self._impl)
- def __getattribute__(self, attr):
- impl = super(JSError, self).__getattribute__("_impl")
- try:
- return getattr(impl, attr)
- except AttributeError:
- return super(JSError, self).__getattribute__(attr)
- _PyV8._JSError._jsclass = JSError
- JSArray = _PyV8.JSArray
- JSExtension = _PyV8.JSExtension
- class JSLocker(_PyV8.JSLocker):
- def __enter__(self):
- self.enter()
- if JSContext.entered:
- self.leave()
- raise RuntimeError("Lock should be acquired before enter the context")
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- if JSContext.entered:
- self.leave()
- raise RuntimeError("Lock should be released after leave the context")
- self.leave()
- def __nonzero__(self):
- return self.entered()
- class JSUnlocker(_PyV8.JSUnlocker):
- def __enter__(self):
- self.enter()
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- self.leave()
- def __nonzero__(self):
- return self.entered()
- class JSClass(object):
- def __getattr__(self, name):
- if name == 'constructor':
- return JSClassConstructor(self.__class__)
- raise AttributeError(name)
- def toString(self):
- "Returns a string representation of an object."
- return "[object %s]" % self.__class__.__name__
- def toLocaleString(self):
- "Returns a value as a string value appropriate to the host environment's current locale."
- return self.toString()
- def valueOf(self):
- "Returns the primitive value of the specified object."
- return self
- def hasOwnProperty(self, name):
- "Returns a Boolean value indicating whether an object has a property with the specified name."
- return hasattr(self, name)
- def isPrototypeOf(self, obj):
- "Returns a Boolean value indicating whether an object exists in the prototype chain of another object."
- raise NotImplementedError()
- def __defineGetter__(self, name, getter):
- "Binds an object's property to a function to be called when that property is looked up."
- if hasattr(type(self), name):
- setter = getattr(type(self), name).fset
- else:
- setter = None
- setattr(type(self), name, property(fget=getter, fset=setter))
- def __lookupGetter__(self, name):
- "Return the function bound as a getter to the specified property."
- return self.name.fget
- def __defineSetter__(self, name, setter):
- "Binds an object's property to a function to be called when an attempt is made to set that property."
- if hasattr(type(self), name):
- getter = getattr(type(self), name).fget
- else:
- getter = None
- setattr(type(self), name, property(fget=getter, fset=setter))
- def __lookupSetter__(self, name):
- "Return the function bound as a setter to the specified property."
- return self.name.fset
- class JSClassConstructor(JSClass):
- def __init__(self, cls):
- self.cls = cls
- @property
- def name(self):
- return self.cls.__name__
- def toString(self):
- return "function %s() {\n [native code]\n}" % self.name
- def __call__(self, *args, **kwds):
- return self.cls(*args, **kwds)
- class JSDebug(object):
- class FrameData(object):
- def __init__(self, frame, count, name, value):
- self.frame = frame
- self.count = count
- self.name = name
- self.value = value
- def __len__(self):
- return self.count(self.frame)
- def __iter__(self):
- for i in xrange(self.count(self.frame)):
- yield (self.name(self.frame, i), self.value(self.frame, i))
- class Frame(object):
- def __init__(self, frame):
- self.frame = frame
- @property
- def index(self):
- return int(self.frame.index())
- @property
- def function(self):
- return self.frame.func()
- @property
- def receiver(self):
- return self.frame.receiver()
- @property
- def isConstructCall(self):
- return bool(self.frame.isConstructCall())
- @property
- def isDebuggerFrame(self):
- return bool(self.frame.isDebuggerFrame())
- @property
- def argumentCount(self):
- return int(self.frame.argumentCount())
- def argumentName(self, idx):
- return str(self.frame.argumentName(idx))
- def argumentValue(self, idx):
- return self.frame.argumentValue(idx)
- @property
- def arguments(self):
- return FrameData(self, self.argumentCount, self.argumentName, self.argumentValue)
- @property
- def localCount(self, idx):
- return int(self.frame.localCount())
- def localName(self, idx):
- return str(self.frame.localName(idx))
- def localValue(self, idx):
- return self.frame.localValue(idx)
- @property
- def locals(self):
- return FrameData(self, self.localCount, self.localName, self.localValue)
- @property
- def sourcePosition(self):
- return self.frame.sourcePosition()
- @property
- def sourceLine(self):
- return int(self.frame.sourceLine())
- @property
- def sourceColumn(self):
- return int(self.frame.sourceColumn())
- @property
- def sourceLineText(self):
- return str(self.frame.sourceLineText())
- def evaluate(self, source, disable_break = True):
- return self.frame.evaluate(source, disable_break)
- @property
- def invocationText(self):
- return str(self.frame.invocationText())
- @property
- def sourceAndPositionText(self):
- return str(self.frame.sourceAndPositionText())
- @property
- def localsText(self):
- return str(self.frame.localsText())
- def __str__(self):
- return str(self.frame.toText())
- class Frames(object):
- def __init__(self, state):
- self.state = state
- def __len__(self):
- return self.state.frameCount
- def __iter__(self):
- for i in xrange(self.state.frameCount):
- yield self.state.frame(i)
- class State(object):
- def __init__(self, state):
- self.state = state
- @property
- def frameCount(self):
- return int(self.state.frameCount())
- def frame(self, idx = None):
- return JSDebug.Frame(self.state.frame(idx))
- @property
- def selectedFrame(self):
- return int(self.state.selectedFrame())
- @property
- def frames(self):
- return JSDebug.Frames(self)
- def __repr__(self):
- s = StringIO()
- try:
- for frame in self.frames:
- s.write(str(frame))
- return s.getvalue()
- finally:
- s.close()
- class DebugEvent(object):
- pass
- class StateEvent(DebugEvent):
- __state = None
- @property
- def state(self):
- if not self.__state:
- self.__state = JSDebug.State(self.event.executionState())
- return self.__state
- class BreakEvent(StateEvent):
- type = _PyV8.JSDebugEvent.Break
- def __init__(self, event):
- self.event = event
- class ExceptionEvent(StateEvent):
- type = _PyV8.JSDebugEvent.Exception
- def __init__(self, event):
- self.event = event
- class NewFunctionEvent(DebugEvent):
- type = _PyV8.JSDebugEvent.NewFunction
- def __init__(self, event):
- self.event = event
- class Script(object):
- def __init__(self, script):
- self.script = script
- @property
- def source(self):
- return self.script.source()
- @property
- def id(self):
- return self.script.id()
- @property
- def name(self):
- return self.script.name()
- @property
- def lineOffset(self):
- return self.script.lineOffset()
- @property
- def lineCount(self):
- return self.script.lineCount()
- @property
- def columnOffset(self):
- return self.script.columnOffset()
- @property
- def type(self):
- return self.script.type()
- def __repr__(self):
- return "<%s script %s @ %d:%d> : '%s'" % (self.type, self.name,
- self.lineOffset, self.columnOffset,
- self.source)
- class CompileEvent(StateEvent):
- def __init__(self, event):
- self.event = event
- @property
- def script(self):
- if not hasattr(self, "_script"):
- setattr(self, "_script", JSDebug.Script(self.event.script()))
- return self._script
- def __str__(self):
- return str(self.script)
- class BeforeCompileEvent(CompileEvent):
- type = _PyV8.JSDebugEvent.BeforeCompile
- def __init__(self, event):
- JSDebug.CompileEvent.__init__(self, event)
- def __repr__(self):
- return "before compile script: %s\n%s" % (repr(self.script), repr(self.state))
- class AfterCompileEvent(CompileEvent):
- type = _PyV8.JSDebugEvent.AfterCompile
- def __init__(self, event):
- JSDebug.CompileEvent.__init__(self, event)
- def __repr__(self):
- return "after compile script: %s\n%s" % (repr(self.script), repr(self.state))
- onMessage = None
- onBreak = None
- onException = None
- onNewFunction = None
- onBeforeCompile = None
- onAfterCompile = None
- def __init__(self):
- self.seq = 0
- def nextSeq(self):
- seq = self.seq
- self.seq += 1
- return seq
- def isEnabled(self):
- return _PyV8.debug().enabled
- def setEnabled(self, enable):
- dbg = _PyV8.debug()
- if enable:
- dbg.onDebugEvent = self.onDebugEvent
- dbg.onDebugMessage = self.onDebugMessage
- dbg.onDispatchDebugMessages = self.onDispatchDebugMessages
- else:
- dbg.onDebugEvent = None
- dbg.onDebugMessage = None
- dbg.onDispatchDebugMessages = None
- dbg.enabled = enable
- enabled = property(isEnabled, setEnabled)
- def onDebugMessage(self, msg):
- if self.onMessage:
- self.onMessage(json.loads(msg))
- def onDebugEvent(self, type, evt):
- if type == _PyV8.JSDebugEvent.Break:
- if self.onBreak: self.onBreak(JSDebug.BreakEvent(evt))
- elif type == _PyV8.JSDebugEvent.Exception:
- if self.onException: self.onException(JSDebug.ExceptionEvent(evt))
- elif type == _PyV8.JSDebugEvent.NewFunction:
- if self.onNewFunction: self.onNewFunction(JSDebug.NewFunctionEvent(evt))
- elif type == _PyV8.JSDebugEvent.BeforeCompile:
- if self.onBeforeCompile: self.onBeforeCompile(JSDebug.BeforeCompileEvent(evt))
- elif type == _PyV8.JSDebugEvent.AfterCompile:
- if self.onAfterCompile: self.onAfterCompile(JSDebug.AfterCompileEvent(evt))
- def onDispatchDebugMessages(self):
- return True
- def breakForDebug(self):
- _PyV8.debug().debugBreak()
- def breakForCommand(self):
- _PyV8.debug().debugBreakForCommand()
- def sendCommand(self, cmd, *args, **kwds):
- request = json.dumps({
- 'seq': self.nextSeq(),
- 'type': 'request',
- 'command': cmd,
- 'arguments': kwds
- })
- _PyV8.debug().sendCommand(request)
- return request
- def debugContinue(self, action='next', steps=1):
- return self.sendCommand('continue', stepaction=action)
- def stepNext(self, steps=1):
- """Step to the next statement in the current function."""
- return self.debugContinue(action='next', steps=steps)
- def stepIn(self, steps=1):
- """Step into new functions invoked or the next statement in the current function."""
- return self.debugContinue(action='in', steps=steps)
- def stepOut(self, steps=1):
- """Step out of the current function."""
- return self.debugContinue(action='out', steps=steps)
- def stepMin(self, steps=1):
- """Perform a minimum step in the current function."""
- return self.debugContinue(action='out', steps=steps)
- debugger = JSDebug()
- class JSProfiler(_PyV8.JSProfiler):
- Modules = _PyV8.JSProfilerModules
- @property
- def logs(self):
- pos = 0
- while True:
- size, buf = self.getLogLines(pos)
- if size == 0:
- break
- for line in buf.split('\n'):
- yield line
- pos += size
- profiler = JSProfiler()
- class JSEngine(_PyV8.JSEngine):
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- del self
- JSStackTrace = _PyV8.JSStackTrace
- JSStackTrace.Options = _PyV8.JSStackTraceOptions
- JSStackFrame = _PyV8.JSStackFrame
- class JSContext(_PyV8.JSContext):
- def __init__(self, obj=None, extensions=[]):
- if JSLocker.actived:
- self.lock = JSLocker()
- self.lock.enter()
- _PyV8.JSContext.__init__(self, obj, extensions)
- def __enter__(self):
- self.enter()
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- self.leave()
- if hasattr(JSLocker, 'lock'):
- self.lock.leave()
- self.lock = None
- del self
- # contribute by marc boeker <http://code.google.com/u/marc.boeker/>
- def convert(obj):
- if type(obj) == _PyV8.JSArray:
- return [convert(v) for v in obj]
- if type(obj) == _PyV8.JSObject:
- return dict([[str(k), convert(obj.__getattr__(str(k)))] for k in obj.__members__])
- return obj
- class AST:
- Scope = _PyV8.AstScope
- Var = _PyV8.AstVariable
- Type = _PyV8.AstNodeType
- Node = _PyV8.AstNode
- Statement = _PyV8.AstStatement
- Expression = _PyV8.AstExpression
- Breakable = _PyV8.AstBreakableStatement
- Block = _PyV8.AstBlock
- Declaration = _PyV8.AstDeclaration
- Iteration = _PyV8.AstIterationStatement
- DoWhile = _PyV8.AstDoWhileStatement
- While = _PyV8.AstWhileStatement
- For = _PyV8.AstForStatement
- ForIn = _PyV8.AstForInStatement
- ExpressionStatement = _PyV8.AstExpressionStatement
- Continue = _PyV8.AstContinueStatement
- Break = _PyV8.AstBreakStatement
- Return = _PyV8.AstReturnStatement
- WithEnter = _PyV8.AstWithEnterStatement
- WithExit = _PyV8.AstWithExitStatement
- Case = _PyV8.AstCaseClause
- Switch = _PyV8.AstSwitchStatement
- Try = _PyV8.AstTryStatement
- TryCatch = _PyV8.AstTryCatchStatement
- TryFinally = _PyV8.AstTryFinallyStatement
- Debugger = _PyV8.AstDebuggerStatement
- Empty = _PyV8.AstEmptyStatement
- Literal = _PyV8.AstLiteral
- MaterializedLiteral = _PyV8.AstMaterializedLiteral
- Object = _PyV8.AstObjectLiteral
- RegExp = _PyV8.AstRegExpLiteral
- Array = _PyV8.AstArrayLiteral
- CatchExtension = _PyV8.AstCatchExtensionObject
- VarProxy = _PyV8.AstVariableProxy
- Slot = _PyV8.AstSlot
- Property = _PyV8.AstProperty
- Call = _PyV8.AstCall
- CallNew = _PyV8.AstCallNew
- CallRuntime = _PyV8.AstCallRuntime
- Op = _PyV8.AstOperation
- UnaryOp = _PyV8.AstUnaryOperation
- BinOp = _PyV8.AstBinaryOperation
- CountOp = _PyV8.AstCountOperation
- CompOp = _PyV8.AstCompareOperation
- Conditional = _PyV8.AstConditional
- Assignment = _PyV8.AstAssignment
- Throw = _PyV8.AstThrow
- Function = _PyV8.AstFunctionLiteral
- SharedFunction = _PyV8.AstSharedFunctionInfoLiteral
- This = _PyV8.AstThisFunction
- class PrettyPrint(object):
- def __init__(self):
- self.out = StringIO()
- def onFunctionLiteral(self, func):
- print >>self.out, "function ", func.name, "(",
- for i in range(func.scope.num_parameters):
- if i > 0: print ", ",
- print >>self.out, func.scope.parameter(i).name
- print >>self.out, ")"
- print >>self.out, "{"
- print >>self.out, "}"
- def __str__(self):
- return self.out.getvalue()
- import datetime
- import unittest
- import logging
- import traceback
- class TestContext(unittest.TestCase):
- def testMultiNamespace(self):
- self.assert_(not bool(JSContext.inContext))
- self.assert_(not bool(JSContext.entered))
- class Global(object):
- name = "global"
- g = Global()
- with JSContext(g) as ctxt:
- self.assert_(bool(JSContext.inContext))
- self.assertEquals(g.name, str(JSContext.entered.locals.name))
- self.assertEquals(g.name, str(JSContext.current.locals.name))
- class Local(object):
- name = "local"
- l = Local()
- with JSContext(l):
- self.assert_(bool(JSContext.inContext))
- self.assertEquals(l.name, str(JSContext.entered.locals.name))
- self.assertEquals(l.name, str(JSContext.current.locals.name))
- self.assert_(bool(JSContext.inContext))
- self.assertEquals(g.name, str(JSContext.entered.locals.name))
- self.assertEquals(g.name, str(JSContext.current.locals.name))
- self.assert_(not bool(JSContext.entered))
- self.assert_(not bool(JSContext.inContext))
- def _testMultiContext(self):
- # Create an environment
- with JSContext() as ctxt0:
- ctxt0.securityToken = "password"
- global0 = ctxt0.locals
- global0.custom = 1234
- self.assertEquals(1234, int(global0.custom))
- # Create an independent environment
- with JSContext() as ctxt1:
- ctxt1.securityToken = ctxt0.securityToken
- global1 = ctxt1.locals
- global1.custom = 1234
- self.assertEquals(1234, int(global0.custom))
- self.assertEquals(1234, int(global1.custom))
- # Now create a new context with the old global
- with JSContext(global1) as ctxt2:
- ctxt2.securityToken = ctxt1.securityToken
- self.assertRaises(AttributeError, int, global1.custom)
- self.assertRaises(AttributeError, int, global2.custom)
- def _testSecurityChecks(self):
- with JSContext() as env1:
- env1.securityToken = "foo"
- # Create a function in env1.
- env1.eval("spy=function(){return spy;}")
- spy = env1.locals.spy
- self.assert_(isinstance(spy, _PyV8.JSFunction))
- # Create another function accessing global objects.
- env1.eval("spy2=function(){return 123;}")
- spy2 = env1.locals.spy2
- self.assert_(isinstance(spy2, _PyV8.JSFunction))
- # Switch to env2 in the same domain and invoke spy on env2.
- env2 = JSContext()
- env2.securityToken = "foo"
- with env2:
- result = spy.apply(env2.locals)
- self.assert_(isinstance(result, _PyV8.JSFunction))
- env2.securityToken = "bar"
- # Call cross_domain_call, it should throw an exception
- with env2:
- self.assertRaises(JSError, spy2.apply, env2.locals)
- def _testCrossDomainDelete(self):
- with JSContext() as env1:
- env2 = JSContext()
- # Set to the same domain.
- env1.securityToken = "foo"
- env2.securityToken = "foo"
- env1.locals.prop = 3
- env2.locals.env1 = env1.locals
- # Change env2 to a different domain and delete env1.prop.
- #env2.securityToken = "bar"
- self.assertEquals(3, int(env1.eval("prop")))
- print env1.eval("env1")
- with env2:
- self.assertEquals(3, int(env2.eval("this.env1.prop")))
- self.assertEquals("false", str(e.eval("delete env1.prop")))
- # Check that env1.prop still exists.
- self.assertEquals(3, int(env1.locals.prop))
- class TestWrapper(unittest.TestCase):
- def testObject(self):
- with JSContext() as ctxt:
- o = ctxt.eval("new Object()")
- self.assert_(hash(o) > 0)
- o1 = o.clone()
- self.assertEquals(hash(o1), hash(o))
- self.assert_(o != o1)
- def testAutoConverter(self):
- with JSContext() as ctxt:
- ctxt.eval("""
- var_i = 1;
- var_f = 1.0;
- var_s = "test";
- var_b = true;
- """)
- vars = ctxt.locals
- var_i = vars.var_i
- self.assert_(var_i)
- self.assertEquals(1, int(var_i))
- var_f = vars.var_f
- self.assert_(var_f)
- self.assertEquals(1.0, float(vars.var_f))
- var_s = vars.var_s
- self.assert_(var_s)
- self.assertEquals("test", str(vars.var_s))
- var_b = vars.var_b
- self.assert_(var_b)
- self.assert_(bool(var_b))
- attrs = dir(ctxt.locals)
- self.assert_(attrs)
- self.assert_("var_i" in attrs)
- self.assert_("var_f" in attrs)
- self.assert_("var_s" in attrs)
- self.assert_("var_b" in attrs)
- def testExactConverter(self):
- class MyInteger(int, JSClass):
- pass
- class MyString(str, JSClass):
- pass
- class MyUnicode(unicode, JSClass):
- pass
- class MyDateTime(datetime.time, JSClass):
- pass
- class Global(JSClass):
- var_bool = True
- var_int = 1
- var_float = 1.0
- var_str = 'str'
- var_unicode = u'unicode'
- var_datetime = datetime.datetime.now()
- var_date = datetime.date.today()
- var_time = datetime.time()
- var_myint = MyInteger()
- var_mystr = MyString('mystr')
- var_myunicode = MyUnicode('myunicode')
- var_mytime = MyDateTime()
- with JSContext(Global()) as ctxt:
- typename = ctxt.eval("(function (name) { return this[name].constructor.name; })")
- typeof = ctxt.eval("(function (name) { return typeof(this[name]); })")
- self.assertEquals('Boolean', typename('var_bool'))
- self.assertEquals('Number', typename('var_int'))
- self.assertEquals('Number', typename('var_float'))
- self.assertEquals('String', typename('var_str'))
- self.assertEquals('String', typename('var_unicode'))
- self.assertEquals('Date', typename('var_datetime'))
- self.assertEquals('Date', typename('var_date'))
- self.assertEquals('Date', typename('var_time'))
- self.assertEquals('MyInteger', typename('var_myint'))
- self.assertEquals('MyString', typename('var_mystr'))
- self.assertEquals('MyUnicode', typename('var_myunicode'))
- self.assertEquals('MyDateTime', typename('var_mytime'))
- self.assertEquals('object', typeof('var_myint'))
- self.assertEquals('object', typeof('var_mystr'))
- self.assertEquals('object', typeof('var_myunicode'))
- self.assertEquals('object', typeof('var_mytime'))
- def testFunction(self):
- with JSContext() as ctxt:
- func = ctxt.eval("""
- (function ()
- {
- function a()
- {
- return "abc";
- }
- return a();
- })
- """)
- self.assertEquals("abc", str(func()))
- self.assert_(func != None)
- self.assertFalse(func == None)
- func = ctxt.eval("(function test() {})")
- self.assertEquals("test", func.name)
- #TODO fix me, why the setter doesn't work?
- func.name = "hello"
- #self.assertEquals("hello", func.name)
- def testCall(self):
- class Hello(object):
- def __call__(self, name):
- return "hello " + name
- class Global(JSClass):
- hello = Hello()
- with JSContext(Global()) as ctxt:
- self.assertEquals("hello flier", ctxt.eval("hello('flier')"))
- def testJSError(self):
- with JSContext() as ctxt:
- try:
- ctxt.eval('throw "test"')
- self.fail()
- except:
- self.assert_(JSError, sys.exc_type)
- def testErrorInfo(self):
- with JSContext() as ctxt:
- with JSEngine() as engine:
- try:
- engine.compile("""
- function hello()
- {
- throw Error("hello world");
- }
- hello();""", "test", 10, 10).run()
- self.fail()
- except JSError, e:
- self.assert_(str(e).startswith('JSError: Error: hello world ( test @ 14 : 34 ) ->'))
- self.assertEqual("Error", e.name)
- self.assertEqual("hello world", e.message)
- self.assertEqual("test", e.scriptName)
- self.assertEqual(14, e.lineNum)
- self.assertEqual(102, e.startPos)
- self.assertEqual(103, e.endPos)
- self.assertEqual(34, e.startCol)
- self.assertEqual(35, e.endCol)
- self.assertEqual('throw Error("hello world");', e.sourceLine.strip())
- self.assertEqual('Error: hello world\n' +
- ' at Error (unknown source)\n' +
- ' at hello (test:14:35)\n' +
- ' at test:17:25', e.stackTrace)
- def testStackTrace(self):
- class Global(JSClass):
- def GetCurrentStackTrace(self, limit):
- return JSStackTrace.GetCurrentStackTrace(4, JSStackTrace.Options.Detailed)
- with JSContext(Global()) as ctxt:
- st = ctxt.eval("""
- function a()
- {
- return GetCurrentStackTrace(10);
- }
- function b()
- {
- return eval("a()");
- }
- function c()
- {
- return new b();
- }
- c();""", "test")
- self.assertEquals(4, len(st))
- 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))
- self.assertEquals("test.a (4:28)\n. (1:1) eval\ntest.b (8:28) constructor\ntest.c (13:17)",
- "\n".join(["%s.%s (%d:%d)%s%s" % (
- f.scriptName, f.funcName, f.lineNum, f.column,
- ' eval' if f.isEval else '',
- ' constructor' if f.isConstructor else '') for f in st]))
- def testPythonException(self):
- class Global(JSClass):
- def raiseException(self):
- raise RuntimeError("Hello")
- with JSContext(Global()) as ctxt:
- r = ctxt.eval("""
- msg ="";
- try
- {
- this.raiseException()
- }
- catch(e)
- {
- msg += "catch " + e + ";";
- }
- finally
- {
- msg += "finally";
- }""")
- self.assertEqual("catch Error: Hello;finally", str(ctxt.locals.msg))
- def testExceptionMapping(self):
- class Global(JSClass):
- def raiseIndexError(self):
- return [1, 2, 3][5]
- def raiseAttributeError(self):
- None.hello()
- def raiseSyntaxError(self):
- eval("???")
- def raiseTypeError(self):
- int(sys)
- def raiseNotImplementedError(self):
- raise NotImplementedError("Not support")
- with JSContext(Global()) as ctxt:
- ctxt.eval("try { this.raiseIndexError(); } catch (e) { msg = e; }")
- self.assertEqual("RangeError: list index out of range", str(ctxt.locals.msg))
- ctxt.eval("try { this.raiseAttributeError(); } catch (e) { msg = e; }")
- self.assertEqual("ReferenceError: 'NoneType' object has no attribute 'hello'", str(ctxt.locals.msg))
- ctxt.eval("try { this.raiseSyntaxError(); } catch (e) { msg = e; }")
- self.assertEqual("SyntaxError: invalid syntax", str(ctxt.locals.msg))
- ctxt.eval("try { this.raiseTypeError(); } catch (e) { msg = e; }")
- self.assertEqual("TypeError: int() argument must be a string or a number, not 'module'", str(ctxt.locals.msg))
- ctxt.eval("try { this.raiseNotImplementedError(); } catch (e) { msg = e; }")
- self.assertEqual("Error: Not support", str(ctxt.locals.msg))
- def testArray(self):
- with JSContext() as ctxt:
- array = ctxt.eval("""
- var array = new Array();
- for (i=0; i<10; i++)
- {
- array[i] = 10-i;
- }
- array;
- """)
- self.assert_(isinstance(array, _PyV8.JSArray))
- self.assertEqual(10, len(array))
- self.assert_(5 in array)
- self.assertFalse(15 in array)
- l = list(array)
- self.assertEqual(10, len(l))
- for i in xrange(10):
- self.assertEqual(10-i, array[i])
- self.assertEqual(10-i, l[i])
- array[5] = 0
- self.assertEqual(0, array[5])
- del array[5]
- self.assertRaises(IndexError, lambda: array[5])
- ctxt.locals.array1 = JSArray(5)
- ctxt.locals.array2 = JSArray([1, 2, 3, 4, 5])
- for i in xrange(len(ctxt.locals.array2)):
- ctxt.locals.array1[i] = ctxt.locals.array2[i] * 10
- ctxt.eval("""
- var sum = 0;
- for (i=0; i<array1.length; i++)
- sum += array1[i]
- for (i=0; i<array2.length; i++)
- sum += array2[i]
- """)
- self.assertEqual(165, ctxt.locals.sum)
- ctxt.locals.array3 = [1, 2, 3, 4, 5]
- self.assert_(ctxt.eval('array3[1] === 2'))
- self.assert_(ctxt.eval('array3[9] === undefined'))
- def testMultiDimArray(self):
- with JSContext() as ctxt:
- ret = ctxt.eval("""
- ({
- 'test': function(){
- return [
- [ 1, 'abla' ],
- [ 2, 'ajkss' ],
- ]
- }
- })
- """).test()
- self.assertEquals([[1, 'abla'], [2, 'ajkss']], convert(ret))
- def testLazyConstructor(self):
- class Globals(JSClass):
- def __init__(self):
- self.array=JSArray([1,2,3])
- with JSContext(Globals()) as ctxt:
- self.assertEqual(2, ctxt.eval("""array[1]"""))
- def testForEach(self):
- class NamedClass(JSClass):
- foo = 1
- def __init__(self):
- self.bar = 2
- def gen(x):
- yield 0
- yield 1
- yield 2
- with JSContext() as ctxt:
- func = ctxt.eval("""(function (k) {
- var result = [];
- for (var prop in k) {
- result.push(prop);
- }
- return result;
- })""")
- self.assertEquals(["bar"], list(func(NamedClass())))
- self.assertEquals(["0", "1", "2"], list(func([1, 2, 3])))
- self.assertEquals(["1", "2", "3"], list(func({1:1, 2:2, 3:3})))
- self.assertEquals(["0", "1", "2"], list(func(gen(3))))
- def testDict(self):
- import UserDict
- with JSContext() as ctxt:
- obj = ctxt.eval("var r = { 'a' : 1, 'b' : 2 }; r")
- self.assertEqual(1, obj.a)
- self.assertEqual(2, obj.b)
- self.assertEqual({ 'a' : 1, 'b' : 2 }, dict(obj))
- self.assertEqual({ 'a': 1,
- 'b': [1, 2, 3],
- 'c': { 'str' : 'goofy',
- 'float' : 1.234,
- 'obj' : { 'name': 'john doe' }},
- 'd': True,
- 'e': None },
- convert(ctxt.eval("""var x =
- { a: 1,
- b: [1, 2, 3],
- c: { str: 'goofy',
- float: 1.234,
- obj: { name: 'john doe' }},
- d: true,
- e: null }; x""")))
- def testDate(self):
- with JSContext() as ctxt:
- now1 = ctxt.eval("new Date();")
- self.assert_(now1)
- now2 = datetime.datetime.utcnow()
- delta = now2 - now1 if now2 > now1 else now1 - now2
- self.assert_(delta < datetime.timedelta(seconds=1))
- func = ctxt.eval("(function (d) { return d.toString(); })")
- now = datetime.datetime.now()
- self.assert_(str(func(now)).startswith(now.strftime("%a %b %d %Y %H:%M:%S")))
- def testUnicode(self):
- with JSContext() as ctxt:
- self.assertEquals(u"人", unicode(ctxt.eval("\"人\""), "utf-8"))
- self.assertEquals(u"é", unicode(ctxt.eval("\"é\""), "utf-8"))
- func = ctxt.eval("(function (msg) { return msg.length; })")
- self.assertEquals(2, func(u"测试"))
- def testClassicStyleObject(self):
- class FileSystemWarpper:
- @property
- def cwd(self):
- return os.getcwd()
- class Global:
- @property
- def fs(self):
- return FileSystemWarpper()
- with JSContext(Global()) as ctxt:
- self.assertEquals(os.getcwd(), ctxt.eval("fs.cwd"))
- def testRefCount(self):
- count = sys.getrefcount(None)
- class Global(JSClass):
- pass
- with JSContext(Global()) as ctxt:
- ctxt.eval("""
- var none = null;
- """)
- self.assertEquals(count+1, sys.getrefcount(None))
- ctxt.eval("""
- var none = null;
- """)
- self.assertEquals(count+1, sys.getrefcount(None))
- def testProperty(self):
- class Global(JSClass):
- def __init__(self, name):
- self._name = name
- def getname(self):
- return self._name
- def setname(self, name):
- self._name = name
- def delname(self):
- self._name = 'deleted'
- name = property(getname, setname, delname)
- with JSContext(Global('world')) as ctxt:
- self.assertEquals('world', ctxt.eval("name"))
- self.assertEquals('flier', ctxt.eval("name = 'flier';"))
- self.assertEquals('flier', ctxt.eval("name"))
- self.assert_(ctxt.eval("delete name")) # FIXME
- #self.assertEquals('deleted', ctxt.eval("name"))
- ctxt.eval("__defineGetter__('name', function() { return 'fixed'; });")
- self.assertEquals('fixed', ctxt.eval("name"))
- def _testDestructor(self):
- import gc
- owner = self
- owner.deleted = False
- class Hello(object):
- def say(self):
- pass
- def __del__(self):
- owner.deleted = True
- def test():
- with JSContext() as ctxt:
- fn = ctxt.eval("(function (obj) { obj.say(); })")
- obj = Hello()
- self.assert_(2, sys.getrefcount(obj))
- fn(obj)
- self.assert_(3, sys.getrefcount(obj))
- del obj
- test()
- self.assertFalse(owner.deleted)
- JSEngine.collect()
- gc.collect()
- self.assert_(self.deleted)
- def testNullInString(self):
- with JSContext() as ctxt:
- fn = ctxt.eval("(function (s) { return s; })")
- self.assertEquals("hello \0 world", fn("hello \0 world"))
- class TestMultithread(unittest.TestCase):
- def testLocker(self):
- self.assertFalse(JSLocker.actived)
- self.assertFalse(JSLocker.locked)
- with JSLocker() as outter_locker:
- self.assertTrue(JSLocker.actived)
- self.assertTrue(JSLocker.locked)
- self.assertTrue(outter_locker)
- with JSLocker() as inner_locker:
- self.assertTrue(JSLocker.locked)
- self.assertTrue(outter_locker)
- self.assertTrue(inner_locker)
- with JSUnlocker() as unlocker:
- self.assertFalse(JSLocker.locked)
- self.assertTrue(outter_locker)
- self.assertTrue(inner_locker)
- self.assertTrue(JSLocker.locked)
- self.assertTrue(JSLocker.actived)
- self.assertFalse(JSLocker.locked)
- locker = JSLocker()
- with JSContext():
- self.assertRaises(RuntimeError, locker.__enter__)
- self.assertRaises(RuntimeError, locker.__exit__, None, None, None)
- del locker
- def testMultiPythonThread(self):
- import time, threading
- class Global:
- count = 0
- started = threading.Event()
- finished = threading.Semaphore(0)
- def sleep(self, ms):
- time.sleep(ms / 1000.0)
- self.count += 1
- g = Global()
- def run():
- with JSContext(g) as ctxt:
- ctxt.eval("""
- started.wait();
- for (i=0; i<10; i++)
- {
- sleep(100);
- }
- finished.release();
- """)
- threading.Thread(target=run).start()
- now = time.time()
- self.assertEqual(0, g.count)
- g.started.set()
- g.finished.acquire()
- self.assertEqual(10, g.count)
- self.assert_((time.time() - now) >= 1)
- def testMultiJavascriptThread(self):
- import time, thread, threading
- class Global:
- result = []
- def add(self, value):
- with JSUnlocker() as unlocker:
- time.sleep(0.1)
- self.result.append(value)
- g = Global()
- def run():
- with JSContext(g) as ctxt:
- ctxt.eval("""
- for (i=0; i<10; i++)
- add(i);
- """)
- threads = [threading.Thread(target=run), threading.Thread(target=run)]
- with JSLocker():
- for t in threads: t.start()
- for t in threads: t.join()
- self.assertEqual(20, len(g.result))
- def _testPreemptionJavascriptThreads(self):
- import time, thread, threading
- class Global:
- result = []
- def add(self, value):
- # we use preemption scheduler to switch between threads
- # so, just comment the JSUnlocker
- #
- # with JSUnlocker() as unlocker:
- time.sleep(0.1)
- self.result.append(value)
- g = Global()
- def run():
- with JSContext(g) as ctxt:
- ctxt.eval("""
- for (i=0; i<10; i++)
- add(i);
- """)
- threads = [threading.Thread(target=run), threading.Thread(target=run)]
- with JSLocker() as locker:
- JSLocker.startPreemption(100)
- for t in threads: t.start()
- for t in threads: t.join()
- self.assertEqual(20, len(g.result))
- class TestEngine(unittest.TestCase):
- def testClassProperties(self):
- with JSContext() as ctxt:
- self.assert_(str(JSEngine.version).startswith("3."))
- self.assertFalse(JSEngine.dead)
- def testCompile(self):
- with JSContext() as ctxt:
- with JSEngine() as engine:
- s = engine.compile("1+2")
- self.assert_(isinstance(s, _PyV8.JSScript))
- self.assertEquals("1+2", s.source)
- self.assertEquals(3, int(s.run()))
- def testPrecompile(self):
- with JSContext() as ctxt:
- with JSEngine() as engine:
- data = engine.precompile("1+2")
- self.assert_(data)
- self.assertEquals(28, len(data))
- s = engine.compile("1+2", precompiled=data)
- self.assert_(isinstance(s, _PyV8.JSScript))
- self.assertEquals("1+2", s.source)
- self.assertEquals(3, int(s.run()))
- def testExtension(self):
- extSrc = """function hello(name) { return "hello " + name + " from javascript"; }"""
- extJs = JSExtension("hello/javascript", extSrc)
- self.assert_(extJs)
- self.assertEqual("hello/javascript", extJs.name)
- self.assertEqual(extSrc, extJs.source)
- self.assertFalse(extJs.autoEnable)
- self.assertTrue(extJs.registered)
- TestEngine.extJs = extJs
- with JSContext(extensions=['hello/javascript']) as ctxt:
- self.assertEqual("hello flier from javascript", ctxt.eval("hello('flier')"))
- # test the auto enable property
- with JSContext() as ctxt:
- self.assertRaises(JSError, ctxt.eval, "hello('flier')")
- extJs.autoEnable = True
- self.assertTrue(extJs.autoEnable)
- with JSContext() as ctxt:
- self.assertEqual("hello flier from javascript", ctxt.eval("hello('flier')"))
- extJs.autoEnable = False
- self.assertFalse(extJs.autoEnable)
- with JSContext() as ctxt:
- self.assertRaises(JSError, ctxt.eval, "hello('flier')")
- def testNativeExtension(self):
- extSrc = "native function hello();"
- extPy = JSExtension("hello/python", extSrc, lambda func: lambda name: "hello " + name + " from python", register=False)
- self.assert_(extPy)
- self.assertEqual("hello/python", extPy.name)
- self.assertEqual(extSrc, extPy.source)
- self.assertFalse(extPy.autoEnable)
- self.assertFalse(extPy.registered)
- extPy.register()
- self.assertTrue(extPy.registered)
- TestEngine.extPy = extPy
- with JSContext(extensions=['hello/python']) as ctxt:
- self.assertEqual("hello flier from python", ctxt.eval("hello('flier')"))
- def _testSerialize(self):
- data = None
- self.assertFalse(JSContext.entered)
- with JSContext() as ctxt:
- self.assert_(JSContext.entered)
- #ctxt.eval("function hello(name) { return 'hello ' + name; }")
- data = JSEngine.serialize()
- self.assert_(data)
- self.assert_(len(data) > 0)
- self.assertFalse(JSContext.entered)
- #JSEngine.deserialize()
- self.assert_(JSContext.entered)
- self.assertEquals('hello flier', JSContext.current.eval("hello('flier');"))
- def testEval(self):
- with JSContext() as ctxt:
- self.assertEquals(3, int(ctxt.eval("1+2")))
- def testGlobal(self):
- class Global(JSClass):
- version = "1.0"
- with JSContext(Global()) as ctxt:
- vars = ctxt.locals
- # getter
- self.assertEquals(Global.version, str(vars.version))
- self.assertEquals(Global.version, str(ctxt.eval("version")))
- self.assertEquals(None, ctxt.eval("nonexists"))
- # setter
- self.assertEquals(2.0, float(ctxt.eval("version = 2.0")))
- self.assertEquals(2.0, float(vars.version))
- def testThis(self):
- class Global(JSClass):
- version = 1.0
- with JSContext(Global()) as ctxt:
- self.assertEquals("[object Global]", str(ctxt.eval("this")))
- self.assertEquals(1.0, float(ctxt.eval("this.version")))
- def testObjectBuildInMethods(self):
- class Global(JSClass):
- version = 1.0
- with JSContext(Global()) as ctxt:
- self.assertEquals("[object Global]", str(ctxt.eval("this.toString()")))
- self.assertEquals("[object Global]", str(ctxt.eval("this.toLocaleString()")))
- self.assertEquals(Global.version, float(ctxt.eval("this.valueOf()").version))
- self.assert_(bool(ctxt.eval("this.hasOwnProperty(\"version\")")))
- self.assertFalse(ctxt.eval("this.hasOwnProperty(\"nonexistent\")"))
- def testPythonWrapper(self):
- class Global(JSClass):
- s = [1, 2, 3]
- d = {'a': {'b': 'c'}, 'd': ['e', 'f']}
- g = Global()
- with JSContext(g) as ctxt:
- ctxt.eval("""
- s[2] = s[1] + 2;
- s[0] = s[1];
- delete s[1];
- """)
- self.assertEquals([2, 4], g.s)
- self.assertEquals('c', ctxt.eval("d.a.b"))
- self.assertEquals(['e', 'f'], ctxt.eval("d.d"))
- ctxt.eval("""
- d.a.q = 4
- delete d.d
- """)
- self.assertEquals(4, g.d['a']['q'])
- self.assertEquals(None, ctxt.eval("d.d"))
- class TestDebug(unittest.TestCase):
- def setUp(self):
- self.engine = JSEngine()
- def tearDown(self):
- del self.engine
- events = []
- def processDebugEvent(self, event):
- try:
- logging.debug("receive debug event: %s", repr(event))
- self.events.append(repr(event))
- except:
- logging.error("fail to process debug event")
- logging.debug(traceback.extract_stack())
- def testEventDispatch(self):
- global debugger
- self.assert_(not debugger.enabled)
- debugger.onBreak = lambda evt: self.processDebugEvent(evt)
- debugger.onException = lambda evt: self.processDebugEvent(evt)
- debugger.onNewFunction = lambda evt: self.processDebugEvent(evt)
- debugger.onBeforeCompile = lambda evt: self.processDebugEvent(evt)
- debugger.onAfterCompile = lambda evt: self.processDebugEvent(evt)
- with JSContext() as ctxt:
- debugger.enabled = True
- self.assertEquals(3, int(ctxt.eval("function test() { text = \"1+2\"; return eval(text) } test()")))
- debugger.enabled = False
- self.assertRaises(JSError, JSContext.eval, ctxt, "throw 1")
- self.assert_(not debugger.enabled)
- self.assertEquals(4, len(self.events))
- class TestProfile(unittest.TestCase):
- def _testStart(self):
- self.assertFalse(profiler.started)
- profiler.start()
- self.assert_(profiler.started)
- profiler.stop()
- self.assertFalse(profiler.started)
- def _testResume(self):
- self.assert_(profiler.paused)
- self.assertEquals(profiler.Modules.cpu, profiler.modules)
- profiler.resume()
- profiler.resume(profiler.Modules.heap)
- # TODO enable profiler with resume
- #self.assertFalse(profiler.paused)
- class TestAST(unittest.TestCase):
- class Checker(object):
- def __init__(self, testcase):
- self.testcase = testcase
- self.called = 0
- def __getattr__(self, name):
- return getattr(self.testcase, name)
- def test(self, script):
- with JSContext() as ctxt:
- JSEngine().compile(script).visit(self)
- return self.called
- def testBlock(self):
- class BlockChecker(TestAST.Checker):
- def onBlock(self, stmt):
- self.called += 1
- self.assertEquals(AST.Type.Block, stmt.type)
- self.assert_(stmt.initializerBlock)
- self.assertFalse(stmt.anonymous)
- target = stmt.breakTarget
- self.assert_(target)
- self.assertFalse(target.bound)
- self.assert_(target.unused)
- self.assertFalse(target.linked)
- label = stmt.breakTarget.entryLabel
- self.assert_(label)
- self.assertFalse(label.bound)
- self.assert_(label.unused)
- self.assertFalse(label.linked)
- self.assertEquals(2, len(stmt.statements))
- self.assertEquals(['%InitializeVarGlobal("i");', '%InitializeVarGlobal("j");'], [str(s) for s in stmt.statements])
- self.assertEquals(1, BlockChecker(self).test("var i, j;"))
- def testIfStatement(self):
- class IfStatementChecker(TestAST.Checker):
- def onIfStatement(self, stmt):
- self.called += 1
- self.assert_(stmt)
- self.assertEquals(AST.Type.IfStatement, stmt.type)
- self.assertEquals(7, stmt.pos)
- stmt.pos = 100
- self.assertEquals(100, stmt.pos)
- self.assert_(stmt.hasThenStatement)
- self.assert_(stmt.hasElseStatement)
- self.assertEquals("((value%2)==0)", str(stmt.condition))
- self.assertEquals("{ s = \"even\"; }", str(stmt.thenStatement))
- self.assertEquals("{ s = \"odd\"; }", str(stmt.elseStatement))
- self.assertFalse(stmt.condition.trivial)
- self.assertFalse(stmt.condition.propertyName)
- self.assertFalse(stmt.condition.loopCondition)
- self.assertEquals(1, IfStatementChecker(self).test("var s; if (value % 2 == 0) { s = 'even'; } else { s = 'odd'; }"))
- def testForStatement(self):
- class ForStatementChecker(TestAST.Checker):
- def onForStatement(self, stmt):
- self.called += 1
- self.assertEquals("{ j += i; }", str(stmt.body))
- self.assertEquals("i = 0;", str(stmt.init))
- self.assertEquals("(i<10)", str(stmt.condition))
- self.assertEquals("(i++);", str(stmt.next))
- target = stmt.continueTarget
- self.assert_(target)
- self.assertFalse(target.bound)
- self.assert_(target.unused)
- self.assertFalse(target.linked)
- self.assertFalse(stmt.fastLoop)
- self.assertEquals(1, ForStatementChecker(self).test("var j; for (i=0; i<10; i++) { j+=i; }"))
- def testForInStatement(self):
- class ForInStatementChecker(TestAST.Checker):
- def onForInStatement(self, stmt):
- self.called += 1
- self.assertEquals("{ out += name; }", str(stmt.body))
- self.assertEquals("name", str(stmt.each))
- self.assertEquals("names", str(stmt.enumerable))
- self.assertEquals(1, ForInStatementChecker(self).test("var names = new Array(); var out = ''; for (name in names) { out += name; }"))
- def testWhileStatement(self):
- class WhileStatementChecker(TestAST.Checker):
- def onWhileStatement(self, stmt):
- self.called += 1
- self.assertEquals("{ i += 1; }", str(stmt.body))
- self.assertEquals("(i<10)", str(stmt.condition))
- self.assertEquals(1, WhileStatementChecker(self).test("var i; while (i<10) { i += 1; }"))
- def testDoWhileStatement(self):
- class DoWhileStatementChecker(TestAST.Checker):
- def onDoWhileStatement(self, stmt):
- self.called += 1
- self.assertEquals("{ i += 1; }", str(stmt.body))
- self.assertEquals("(i<10)", str(stmt.condition))
- self.assertEquals(28, stmt.conditionPos)
- self.assertEquals(1, DoWhileStatementChecker(self).test("var i; do { i += 1; } while (i<10);"))
- def testCallStatements(self):
- class CallStatementChecker(TestAST.Checker):
- def onBlock(self, block):
- for stmt in block.statements:
- stmt.visit(self)
- def onExpressionStatement(self, stmt):
- stmt.expression.visit(self)
- def onCall(self, expr):
- self.called += 1
- self.assertEquals("hello", str(expr.expression))
- self.assertEquals(['"flier"'], [str(arg) for arg in expr.args])
- self.assertEquals(143, expr.pos)
- def onCallNew(self, expr):
- self.called += 1
- self.assertEquals("dog", str(expr.expression))
- self.assertEquals(['"cat"'], [str(arg) for arg in expr.args])
- self.assertEquals(171, expr.pos)
- def onCallRuntime(self, expr):
- self.called += 1
- self.assertEquals("InitializeVarGlobal", expr.name)
- self.assertEquals(['"s"'], [str(arg) for arg in expr.args])
- self.assertFalse(expr.isJsRuntime)
- self.assertEquals(3, CallStatementChecker(self).test("""
- var s;
- function hello(name) { s = "Hello " + name; }
- function dog(name) { this.name = name; }
- hello("flier");
- new dog("cat");
- """))
- def testTryStatements(self):
- class TryStatementsChecker(TestAST.Checker):
- def onBlock(self, block):
- for stmt in block.statements:
- stmt.visit(self)
- def onExpressionStatement(self, stmt):
- stmt.expression.visit(self)
- def onThrow(self, expr):
- self.called += 1
- self.assertEquals('"abc"', str(expr.exception))
- self.assertEquals(54, expr.pos)
- def onTryCatchStatement(self, stmt):
- self.called += 1
- self.assertEquals("{ throw \"abc\"; }", str(stmt.tryBlock))
- self.assertEquals([], stmt.targets)
- stmt.tryBlock.visit(self)
- self.assertEquals(".catch-var", str(stmt.catchVar))
- self.assertEquals("{ <enter with> ({ \"err\": .catch-var }) try { { s = err; } } finally { <exit with> } }", str(stmt.catchBlock))
- def onTryFinallyStatement(self, stmt):
- self.called += 1
- self.assertEquals("{ throw \"abc\"; }", str(stmt.tryBlock))
- self.assertEquals([], stmt.targets)
- self.assertEquals("{ s += \".\"; }", str(stmt.finallyBlock))
- self.assertEquals(3, TryStatementsChecker(self).test("""
- var s;
- try {
- throw "abc";
- }
- catch (err) {
- s = err;
- };
- try {
- throw "abc";
- }
- finally {
- s += ".";
- }
- """))
- def testPrettyPrint(self):
- pp = PrettyPrint()
- with JSContext() as ctxt:
- script = JSEngine().compile("function hello(name) { return 'hello ' + name; }")
- script.visit(pp)
- self.assertEquals("", str(pp))
- if __name__ == '__main__':
- if "-v" in sys.argv:
- level = logging.DEBUG
- else:
- level = logging.WARN
- if "-p" in sys.argv:
- sys.argv.remove("-p")
- print "Press any key to continue..."
- raw_input()
- logging.basicConfig(level=level, format='%(asctime)s %(levelname)s %(message)s')
- logging.info("testing PyV8 module %s with V8 v%s", __version__, JSEngine.version)
- unittest.main()
|