Integration
SDK 5.01x Errata
Fixes Affecting Users of the ScriptEase
ISDKs
API Errata, version 5.01b
New, June 8, 2004
API Errata, version 5.01a
New, March 19, 2004
The
Details
for
5.01b -- (may
apply to earlier version)
- inner function may cause assertion in Call.jsrc
(for ISDK/Java 5.01b)
Issue:
A function containing an inner function may cause an assertion to be thrown around line 2153 (`SEVAR_GET_TYPE(wv)>=VReference').
Fix : In COM/Nombas/jse/Isdk/Call.jsrc around line 9619 there is a block of code like this:
if( (flags&SE.START) != 0 )
{
EvalFuncStart start_info = new EvalFuncStart();
if( start_info==null )
On the line just above that if statement, add this statement:
SEVAR_INIT_UNDEFINED(CALL_NEWSCOPE(this));
- string.replace()
may get caught in near-infinite loop
(for ISDK/Java 5.01b)
Issue:
If string.replace() is called using the global
flag and such that the result of the search
expression replaces the null-patch at the end
of the line, then replace may run for a long
long while (until a memory object is returned).
This is an example of a problem script.
"a".replace( /a*/g, "stuff" );
Fix : In COM/Nombas/jse/libraries/ECMAString.jsrc make
the following changes:
1)
near line 872 replace the definition of the
replace_string() method from this:
private static void replace_string(...
to
this
private static int replace_string(...
2)
add the following return statement at the end of the replace_string() method
(around line 1042):
return replace_text.length();
3)
in function string_search_helper(), near
line 1272, follow this line:
SEObject eval_ret = se.seGetObject(SE.RETURN,SE.VALUE);
with
this additional line
int replace_len = 0;
and
about seven lines below that replace
replace_string(se,se.seGetObject(eval_ret,SE.VALUE) ...
with
replace_len = replace_string(se,se.seGetObject(eval_ret,SE.VALUE) ...
4)
around line 3788 of the same function replace
this block
if( new_index==last_index )
{
new_index++;
with
this
if( new_index == (last_index+replace_len) )
{
new_index = 1 + (last_index+replace_len);
-
string.replace()
incorrect if first arguments is not a RegExp
object
(for ISDK/Java 5.01b)
Bug: If the first parameter to String.prototype.replace()
is not a RegExp object then it will be compiled
into a RegExp object. This is incorrect behavior
and based on an incomplete version of the EcmaScript
specification. Instead, if the first arguments
is not a RegExp object then it should be converted
into a string and a direct string replacement
should follow.
Fix : In COM/Nombas/jse/libraries/ECMAString.jsrc, replace
the function Ecma_String_replace() at about
line 1364 with both functions in the code at ftp://ftp.nombas.com/pub/isdkeval/se501/ecma_string_replace.jsrc
for
5.01a --
- seGetSourceFunc() callback being called an extra time when script is missing a closing brace
(for ISDK/Java 5.01a)
Issue:
When a script that is missing a closing brace ('}') is being compiled by
the ScriptEase engine, the seGetSourceFunc() method of the SEGetSourceFunc
interface gets called by the engine with the SE.SOURCE_GETLINE flag one time too
many. That is to say, after the seGetSourceFunc() has been called with the
SE.SOURCE_GETLINE flag and has returned false once, on the next call to
seGetSourceFunc() the ScriptEase engine should pass it the SE.SOURCE_CLOSE flag,
but instead the engine calls it one more time passing it the SE.SOURCE_GETLINE
flag before calling it a final time passing it the SE.SOURCE_CLOSE flag. This
can be a source of error if the seGetSourceFunc()'s SE.SOURCE_GETLINE handler
does not expect to be called anymore once it has returned false to the
ScriptEase engine.
Fix:
To fix this issue, go to the statement() method in
COM/Nombas/jse/Isdk/SECompile.jsrc. Within the switch statement, there is a case
statement for the character '{'. In that case statement (at approx. line 3915),
add the following code inside the while() loop, before the code already in that
loop:
if ( tokType(this.token)==seTokEOF )
{
success = false;
call.quit(TextCore.BAD_PRIMARY);
break;
}
So now your while() loop should look like this:
while( tokType(this.token)!='}' )
{
if ( tokType(this.token)==seTokEOF )
{
success = false;
call.quit(TextCore.BAD_PRIMARY);
break;
}
if( !this.statement() )
{
success = false;
break;
}
}
- seMustPutXXX() and seMustPutDirectXXX() unsetting most variable attributes
(for ISDK/Java 5.01a)
Bug:
The seMustPutXXX() and seMustPutDirectXXX() functions strip variable
attributes, such as SE.DONTENUM and SE.READONLY, from the variable on which they
are called.
Fix:
A simple workaround is to call seSetAttribs after calling seMustPutXXX()
and seMustPutDirectXXX(). Or to fix the source code in
COM/Nombas/jse/Isdk/Call.jsrc, method sePutMember(), at about line 7188
replace:
obj.newMember(this,name,tmp,
SE.DEFAULT,SE_OBJECT.SE_NM_UPDATE);
with:
obj.newMember(this,name,tmp,
attrib,SE_OBJECT.SE_NM_UPDATE);
Also, at about line 7251 replace:
obj.newMember(this,name,tmp,
SE.DEFAULT,SE_OBJECT.SE_NM_UPDATE);
with:
obj.newMember(this,name,tmp,
attrib,SE_OBJECT.SE_NM_UPDATE);
- sePutWrapper() ignoring the variable attributes
(for ISDK/Java 5.01a)
Bug:
The sePutWrapper() variable attributes (such as SE.DONTENUM) are being ignored.
Fix:
A simple workaround is to call seSetAttribs after calling
sePutWrapper(). Or to fix the source code in COM/Nombas/jse/Isdk/Call.jsrc,
method sePutWrapperEx(), at about line 9003 replace:
ret = call.sePutMember(obj,memdesc,flags);
with:
if ( !call.sePutMember(obj,memdesc,flags) || SE.DEFAULT != desc.VarAttrFlags )
this.seSetAttribs(obj,memdesc,desc.VarAttrFlags);
- Date
parser not recognizing 12 AM or "UTC"
(for ISDK/Java 5.01a)
Problem:
When parsing a date/time with "12:00 AM
UTC" the date parser is not recognizing
this as midnight, but is instead registering
that time as noon. Also, although the ECMAScript
specification sets "GMT" as the tag
for declaring universal time, it has become
common to also use "UTC".
Change:
In COM/Nombas/jse/libraries/SEDate.jsrc, method
do_parse(String), at around line 632, just before
the comment that begins "/* if there is a PM
anywhere..." add this code:
/* if it is 12, but 12 AM, then that time is really 0 */
if ( (time[0] == 12) && (DateBuf.indexOf("am")!= -1) )
time[0] = 0;
and
then around line 758 change this code:
gmt = DateBuf.indexOf("gmt");
if ( gmt == -1 )
to
this:
if ( (-1 == (gmt = DateBuf.indexOf("gmt")))
&& (-1 == (gmt = DateBuf.indexOf("utc"))) )
- Function
constructor causes swapping for created functions
(for ISDK/Java 5.01a)
Problem:
When JSE_MULTIPLE_GLOBAL is defined (the default),
any anonymous function created at runtime with
the Function constructor will preserve the global
from when the Function library was loaded (usually
at program initialization). This will cause
the global to change to the original global
whenever that anonymous function is later called.
Change:
To keep the Function constructor from restoring
its own global replace add the SE_KEEP_GLOBAL
line 2294 of COM/Nombas/jse/libraries/ECMAObject.jsrc
so it becomes:
SE.CLASS( JseStr.Function, WRAPPER_FUNC(Ecma_Function_construct), 0, -1,
SE.SECURE|SE.KEEP_GLOBAL, SE.DONTENUM ),
- String
match function should return null on no match
(for ISDK/Java 5.01a)
Issue:
According to the commonly recognized ECMAScript
Edition 3 Errata, string.match should return
NULL if there are no items matched. This differs
from the ECMAScript document, and from our 5.01a
code, which would return a zero-length array.
Change:
To get the return-null behavior, In COM/Nombas/jse/libraries/ECMAString.jsrc,
near the end of string_which_search_helper()
(at about line 1334) replace this code:
if( mode==SE_MATCH_MODE )
{
se.sePutObject(SE.RETURN,SE.VALUE,ret);
}
with
if( mode==SE_MATCH_MODE )
{
if ( array_index == 0 )
{
/* this part of the code is a result of the ECMA-262 errata */
se.sePutNull(SE.RETURN,SE.VALUE);
}
else
{
se.sePutObject(SE.RETURN,SE.VALUE,ret);
}
}
- seGetSourceFunc(...seSourceGetLine...)
called twice if first call returns False
(for ISDK/Java 5.01a)
Bug:
If seGetSourceFunc(...SE.SOURCE_GETLINE...)
returns false the first time it is called, it
will be called again a second time.
Fix:
In COM/Nombas/jse/Isdk/Source.jsrc, method newFromFile(),
change the code after !This.nextLine() (around
line 190) from:
This.MemoryPtr = new SourcePtr("\0");
to
Success[0] = false;
- string.split
returns undefined element 0 if no match
(for ISDK/Java 5.01a)
Bug:
If String.prototype.split(delimeter) is called,
but there is no delimeter matched, then this
function should return a 1-element array with
the original string as element 0. Instead it
is wrongly returning and undefined type as element
0.
Fix:
In COM/Nombas/jse/libraries/ECMAString.jsrc,
function Ecma_String_split, at about line 425
this statement:
se.seAssign(ret,SE.NUM(se.seGetLong(ret,SE.STOCK(JseStrID.length))),
SE.WRAPPER_TEMP, SE.MEM("temp"));
should
be changed to:
se.sePutString(ret,SE.NUM(se.seGetLong(ret,SE.STOCK(JseStrID.length))),str);
- converting
a recursive array to a string can crash
(for ISDK/Java 5.01a)
Bug:
In an array contains a recursive element that
recursively refers back to the array, and if
the array is then converted to a string, then
the scriptease engine will crash. The following
script demonstrates the problem:
var a = new Array( "a", "b", "c" );
a[1] = new Array( a, 2, 3 );
var foo = "" + a; // will crash here from recursion
Fix:
In COM/Nombas/jse/libraries/ECMAObject.jsrc,
method Ecma_Array_join, after the declaration
of variables (around line 624) add this block:
/* if this object was already visited, then just return a blank string */
if ( se.seExists(SE.THIS,SE.HIDDEN_MEM(PREVENT_RECURSION_NAME)) )
{
se.sePutString(SE.RETURN,SE.VALUE,"");
return;
}
se.sePutBool(SE.THIS,SE.HIDDEN_MEM(PREVENT_RECURSION_NAME),true);
and
add the following as the final line of function
Ecma_Array_join:
se.seDelete(SE.THIS, SE.HIDDEN_MEM(PREVENT_RECURSION_NAME));
Finally,
add this line outside the of the Ecma_Array_join
method:
private static final String PREVENT_RECURSION_NAME = "join recursed";
- no
flag provided to bypass dynamic callbacks, but
allow for prototype chain
(for ISDK/Java 5.01a)
Bug:
SE.GF_DIRECT (as used by the seGetDirect and
seGetEx calls) bypasses the dynamic callbacks
and the prototype chain. SE.GF_NOPROTOTYPE bypasses
the prototype chain but not the callbacks. There
is no corresponding call to skip dynamic callbacks
but still allow access via the prototype chain.
Fix:
Add a new SE.GF_NOCALLBACKS option to bypass
callbacks but still allow access via the prototype
chain. This fix assumes that the
HP_DIRECTCHECK errata has already been applied.
In COM/Nombas/jse/Isdk/SE.jsrc
at about line 113 replace this line:
public final static int GF_DIRECT = 0x001;
with
these two lines:
public final static int GF_NOCALLBACKS = 0x001;
public final static int GF_DIRECT = (GF_NOPROTOTYPE|GF_NOCALLBACKS);
and
in COM/Nombas/jse/Isdk/AbstractVar.jsrc near
line 67 and in COM/Nombas/jse/Isdk/_SEVar.jsrc
near line 279 change
static final int GV_NO_PROTOTYPE = SE.GF_NOPROTOTYPE;
static final int GV_NO_DYNAMIC = SE.GF_NOCALLBACKS;
to
static final int GV_NO_PROTOTYPE = SE.GF_NOPROTOTYPE;
static final int GV_NO_DYNAMIC = SE.GF_NOCALLBACKS;
and
then in COM/Nombas/jse/Isdk/Call.jsrc and COM/Nombas/jse/Isdk/SEContext.jsrc
change every reference from
SE.GF_DIRECT
to
SE.GF_NOCALLBACKS
and
also in COM/Nombas/jse/Isdk/Call.jsrc, function
notThere(), at around line 6593 change
direct = true;
to
flags |= SE.GF_NOCALLBACKS;
finally,
about 17 lines later, replace this block:
int f = ( ((flags & SE.GF_DIRECT) != 0) || direct )
? ( SE_VAR.GV_NO_PROTOTYPE | SE_VAR.GV_NO_DYNAMIC )
: ( ( (flags & SE.GF_NOPROTOTYPE) != 0 ) ? SE_VAR.GV_NO_PROTOTYPE :
SE_VAR.GV_DEFAULT );
SEVAR_INIT_OBJECT(tmpobj,obj);
ret = tmpobj.getValue(this,name,this.api_temp,f);
with
this
SEVAR_INIT_OBJECT(tmpobj,obj);
ret = tmpobj.getValue(this,name,this.api_temp,flags);
- calling
byref function turns all local variables into
references
(for ISDK/Java 5.01a)
Bug:
If a byref function is called from within another
function, all of the local variables in the
outer function will be turned into references.
Fix:
In COM/Nombas/jse/Isdk/Secode.jsrc, method interpret(),
near line 1002 this block of code:
if( FUNCTION_PASSBYREF(function) ||
(FUNCTION_IS_LOCAL(function) &&
with_tmp < ((LocalFunction)function).localItems.InputParameterCount &&
((LocalFunction)function).localItems.items[with_tmp].VarAttrib != 0) )
{
/* In this case we have pass-by-reference */
SEVAR_INIT_UNDEFINED(w_lhs);
if( CALL_VAROBJ(call)==null )
call.createVariableObject(null,0);
}
SEVAR_COPY(w_lhs,w_rhs);
break;
should
be changed to this code:
if( FUNCTION_PASSBYREF(function) ||
(FUNCTION_IS_LOCAL(function) &&
with_tmp < ((LocalFunction)function).localItems.InputParameterCount &&
((LocalFunction)function).localItems.items[with_tmp].VarAttrib != 0) )
{
/* In this case we have pass-by-reference */
SEVAR_INIT_UNDEFINED(w_lhs);
if( CALL_VAROBJ(call)==null )
call.createVariableObject(null,0);
SEVAR_COPY(w_lhs,w_rhs);
}
else
{
/* The local variable could be a reference into the variable object.
* If this is true, it will be passed by reference even though it
* should not be. To avoid this, we now copy the variable to the stack
* and dereference it.
*/
SEVAR_COPY(w_lhs,w_rhs);
SEVAR_DEREFERENCE(call,w_lhs);
}
break;
- too
many callbacks for global get and for hasprop
returning HP_DIRECTCHECK
(for ISDK/Java 5.01a)
Bug:
If the global object has a get callback, then
get is called too often while initializing code.
Also, if any hasProp callback is returning HP_DIRECTCHECK
then the get callback is being called, although
it shouldn't.
Fix:
In COM/Nombas/jse/Isdk/Call.jsrc, method function(),
near line 2122 this block of code:
SE_VAR wLoc = STACK_PUSH(this);
SE_VAR wTmp = STACK_PUSH(this);
SEVAR_INIT_UNDEFINED(wTmp);
SEVAR_INIT_OBJECT(wLoc,CALL_VAROBJ(this));
wLoc.getDotNamedVar(this,this.GetStringTableEntry(ourname),
SE.DEFAULT);
if( hif_func!=null )
{
SEVAR_INIT_OBJECT(wTmp,hif_func);
if ( VObject == SEVAR_GET_TYPE(CALL_NEWSCOPE(this)) )
{
SE_OBJECT hobj;
hobj = SEVAR_GET_OBJECT(CALL_NEWSCOPE(this));
assert( null != hobj );
if( 0<hobj.used )
{
wTmp.data = hobj;
}
}
}
else
{
SEVAR_INIT_UNDEFINED(wTmp);
}
should
be changed to this code:
SE_VAR wLoc = STACK_PUSH(this);
SE_VAR wTmp = STACK_PUSH(this);
if( hif_func!=null )
{
SEVAR_INIT_OBJECT(wTmp,hif_func);
SEVAR_INIT_OBJECT(wLoc,CALL_VAROBJ(this));
wLoc.getDotNamedVar(this,this.GetStringTableEntry(ourname),
SE.DEFAULT);
if ( VObject == SEVAR_GET_TYPE(CALL_NEWSCOPE(this)) )
{
SE_OBJECT hobj;
hobj = SEVAR_GET_OBJECT(CALL_NEWSCOPE(this));
assert( null != hobj );
if( 0<hobj.used )
{
wTmp.data = hobj;
}
}
}
else
{
SEVAR_INIT_UNDEFINED(wTmp);
SEVAR_INIT_REFERENCE(wLoc,CALL_VAROBJ(this),ourname);
}
Then
in COM/Nombas/jse/Isdk/Call.jsrc method notThere(),
near line 6566 replace the else block that begins
with this line:
SE_VAR tmpobj = STACK_PUSH(this);
with
this block
SE_VAR tmpobj = STACK_PUSH(this);
direct = false;
# if JSE_DYNAMIC_CALLBACKS==1
ret = true;
SEVAR_INIT_UNDEFINED(tmpobj);
/* If the object says it does not have the property,
* believe it. Return false with the undefined value.
*/
if( (flags&SE.GF_NOCALLBACKS)==0
&& SEOBJ_IS_DYNAMIC_PROP(this,obj,SEHasPropCallback)
&& !this.Global.stringTable.isHiddenProp(name) )
{
if( obj.callDynamicProperty(this,SE.HASPROP_CALLBACK,
name,null,tmpobj) )
{
if ( SE.HP_HASNOT == SEVAR_GET_SLONG(tmpobj) )
{
ret = false;
SEVAR_INIT_UNDEFINED(this.api_temp);
}
}
else
{
if ( VNumber == SEVAR_GET_TYPE(tmpobj) )
{
if ( SE.HP_DIRECTCHECK == SEVAR_GET_SLONG(tmpobj) )
direct = true;
}
}
}
if( ret )
# endif
{
int f = ( ((flags & SE.GF_DIRECT) != 0) || direct )
? ( SE_VAR.GV_NO_PROTOTYPE | SE_VAR.GV_NO_DYNAMIC )
: ( ( (flags & SE.GF_NOPROTOTYPE) != 0 ) ? SE_VAR.GV_NO_PROTOTYPE :
SE_VAR.GV_DEFAULT );
SEVAR_INIT_OBJECT(tmpobj,obj);
ret = tmpobj.getValue(this,name,this.api_temp,f);
}
STACK_POP(this);
And
in COM/Nombas/jse/Isdk/_SEObject.jsrc, method
seobjCallDynamicProperty(), replace "case SE.HASPROP_CALLBACK:"
around line 1097 with this code:
case SE.HASPROP_CALLBACK:
{
int cp = DYNA_CALLBACK1(((SEHasPropCallback)cb).hasProp,prop_name);
if ( cp < 0 )
{
assert( cp==SE.HP_CHECK || cp==SE.HP_DIRECTCHECK );
done = false;
}
else
{
assert( cp==SE.HP_HAS || cp==SE.HP_HASNOT );
}
assert( result!=null );
SEVAR_INIT_SLONG(result_tmp,cp);
break;
}
then
around line 1354 replace these lines (in the
hasProperty()) method:
STACK_POP(this);
/* Call the 'get' to get the property */
with
this block
else
{
/* that hasprop callback may have returned HP_DIRECTCHECK which means we act as
* if there were no callbacks at all
*/
if ( VNumber == SEVAR_GET_TYPE(value) )
{
if ( SE.HP_DIRECTCHECK == SEVAR_GET_SLONG(value) )
{
//STACK_POP(call); /* value */
goto_standard_directcheck = true;
}
}
}
STACK_POP(call); /* value */
/* Call the 'get' to get the property */
next,
around line 1380 find this code
/* Call the 'get' to get the property */
/* either no dynamic _hasProperty (!handled) or
* _hasProperty says 'yes it has the property' and
* dest is not NULL, so we need to retrieve the property
* In either case, we have to do a dynamic get because we
* need the value
*/
if ( (rhs=dest)==null )
{
rhs = STACK_PUSH(call);
SEVAR_INIT_OBJECT(rhs,this);
}
/* can't use SEVAR_DEREFERENCE because this
* could be a real dynamic object
*/
# if JSE_ALWAYS_COLLECT==1
/* on some occasions calldyna will happen and GC may happen, so force it always */
Garbage.garbageCollect(call);
# endif
if( this.callDynamicProperty(call,SE.GET_CALLBACK,propname,null,rhs) )
{
/* If hasProperty says yes, then undefined means undefined. Otherwise,
* Undefined is the only way to differentiate a has it from does not
* have it.
*/
boolean ret = (SEVAR_GET_TYPE(rhs)!=VUndefined) || handled;
if( ret && (flags&HP_REFERENCE)!=0 )
{
SEVAR_INIT_REFERENCE(rhs,this,propname);
}
if ( dest==null )
{
STACK_POP(call);
}
return ret;
}
if ( dest==null )
{
STACK_POP(call);
}
and
change it to this (wrap it in an if-block):
if ( !goto_standard_directcheck )
{
/* Call the 'get' to get the property */
/* either no dynamic _hasProperty (!handled) or
* _hasProperty says 'yes it has the property' and
* dest is not NULL, so we need to retrieve the property
* In either case, we have to do a dynamic get because we
* need the value
*/
if ( (rhs=dest)==null )
{
rhs = STACK_PUSH(call);
SEVAR_INIT_OBJECT(rhs,this);
}
/* can't use SEVAR_DEREFERENCE because this
* could be a real dynamic object
*/
# if JSE_ALWAYS_COLLECT==1
/* on some occasions calldyna will happen and GC may happen, so force it always */
Garbage.garbageCollect(call);
# endif
if( this.callDynamicProperty(call,SE.GET_CALLBACK,propname,null,rhs) )
{
/* If hasProperty says yes, then undefined means undefined. Otherwise,
* Undefined is the only way to differentiate a has it from does not
* have it.
*/
boolean ret = (SEVAR_GET_TYPE(rhs)!=VUndefined) || handled;
if( ret && (flags&HP_REFERENCE)!=0 )
{
SEVAR_INIT_REFERENCE(rhs,this,propname);
}
if ( dest==null )
{
STACK_POP(call);
}
return ret;
}
if ( dest==null )
{
STACK_POP(call);
}
}
finally, add this line at the top of the hasProperty()
method
boolean goto_standard_directcheck = false;
|