Skip to content

Commit

Permalink
[MERGE #1319 @suwc] Enable es6toprimitive under experimental
Browse files Browse the repository at this point in the history
Merge pull request #1319 from suwc:build/suwc/buddy

Enable es6toprimitive under experimental flag.
Update following areas to be spec-compliant:

ToPrimitive (abstract operation)
Date.prototype[@@toprimitive]()
Symbol.prototype[@@toprimitive]()

Update unit tests with coverage on:

ToNumber (abstract operation)
ToString (abstract operation)
ToPropertyKey (abstract operation)
Abstract rational comparison (abstract operation)
Abstract equality comparison (abstract operation)
"+" operator
Date() constructor
Date.prototype.toJSON()
  • Loading branch information
Suwei Chen committed Jul 26, 2016
2 parents 118b591 + bf450f4 commit f3c96ad
Show file tree
Hide file tree
Showing 8 changed files with 351 additions and 25 deletions.
5 changes: 4 additions & 1 deletion lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,10 @@ FLAGPR (Boolean, ES6, ES6StringPrototypeFixes, "Enable ES6 String.prot
#define COMPILE_DISABLE_ES6PrototypeChain 0
#endif
FLAGPR_REGOVR_EXP(Boolean, ES6, ES6PrototypeChain , "Enable ES6 prototypes (Example: Date prototype is object)", DEFAULT_CONFIG_ES6PrototypeChain)
FLAGPR (Boolean, ES6, ES6ToPrimitive , "Enable ES6 ToPrimitive symbol" , DEFAULT_CONFIG_ES6ToPrimitive)
#ifndef COMPILE_DISABLE_ES6ToPrimitive
#define COMPILE_DISABLE_ES6ToPrimitive 0
#endif
FLAGPR_REGOVR_EXP(Boolean, ES6, ES6ToPrimitive , "Enable ES6 ToPrimitive symbol" , DEFAULT_CONFIG_ES6ToPrimitive)
FLAGPR (Boolean, ES6, ES6ToLength , "Enable ES6 ToLength fixes" , DEFAULT_CONFIG_ES6ToLength)
FLAGPR (Boolean, ES6, ES6ToStringTag , "Enable ES6 ToStringTag symbol" , DEFAULT_CONFIG_ES6ToStringTag)
FLAGPR (Boolean, ES6, ES6TypedArrayExtensions, "Enable ES6 TypedArray extensions" , DEFAULT_CONFIG_ES6TypedArrayExtensions)
Expand Down
3 changes: 3 additions & 0 deletions lib/Parser/rterrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,6 @@ RT_ERROR_MSG(JSERR_JsonBadNumber, 5654, "JSON.parse Error: Invalid number at pos
RT_ERROR_MSG(JSERR_JsonIllegalChar, 5655, "JSON.parse Error: Invalid character at position:%s", "JSON.parse syntax error", kjstTypeError, 0)
RT_ERROR_MSG(JSERR_JsonBadHexDigit, 5656, "JSON.parse Error: Expected hexadecimal digit at position:%s", "JSON.parse syntax error", kjstTypeError, 0)
RT_ERROR_MSG(JSERR_JsonNoStrEnd, 5657, "JSON.parse Error: Unterminated string constant at position:%s", "JSON.parse syntax error", kjstTypeError, 0)

// Date.prototype[@@toPrimitive] invalid hint
RT_ERROR_MSG(JSERR_InvalidHint, 5658, "%s: invalid hint", "invalid hint", kjstTypeError, 0)
6 changes: 3 additions & 3 deletions lib/Runtime/Language/JavascriptConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,17 +525,17 @@ namespace Js
return CALL_FUNCTION(exoticToPrim, CallInfo(CallFlags_Value, 2), recyclableObject, hintString);
});

Assert(!CrossSite::NeedMarshalVar(result, requestContext));

if (!result)
{
// There was an implicit call and implicit calls are disabled. This would typically cause a bailout.
Assert(threadContext->IsDisableImplicitCall());
return requestContext->GetLibrary()->GetNull();
}

Assert(!CrossSite::NeedMarshalVar(result, requestContext));
}
// If result is an ECMAScript language value and Type(result) is not Object, then return result.
if (TaggedInt::Is(result) || JavascriptOperators::IsExposedType(JavascriptOperators::GetTypeId(result)))
if (TaggedInt::Is(result) || !JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(result)))
{
return result;
}
Expand Down
12 changes: 6 additions & 6 deletions lib/Runtime/Library/JavascriptDate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ namespace Js
//The allowed values for hint are "default", "number", and "string"
if (args.Info.Count == 2)
{
if (!JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(args[0])))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedObject, _u("Date[Symbol.toPrimitive]"));
}

if (JavascriptString::Is(args[1]))
{
JavascriptString* StringObject = JavascriptString::FromVar(args[1]);
Expand All @@ -267,13 +272,8 @@ namespace Js
//anything else should throw a type error
}
}
else if (args.Info.Count == 1)
{
//7.1.1 ToPrimitive Note: Date objects treat no hint as if the hint were String.
return JavascriptConversion::OrdinaryToPrimitive(args[0], JavascriptHint::HintString/*tryFirst*/, scriptContext);
}

JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid, _u("Date[Symbol.toPrimitive]"));
JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidHint, _u("Date[Symbol.toPrimitive]"));
}

Var JavascriptDate::EntryGetDate(RecyclableObject* function, CallInfo callInfo, ...)
Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/Library/JavascriptSymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ namespace Js
}
else
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid, _u("Symbol[Symbol.toPrimitive]"));
JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedSymbol, _u("Symbol[Symbol.toPrimitive]"));
}
}

Expand Down
17 changes: 11 additions & 6 deletions test/es6/ES6Symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,17 @@ var tests = [
assert.areEqual(x, Symbol.prototype[Symbol.toPrimitive].call(x), "x == Symbol.prototype[Symbol.toPrimitive].call(x)");

// TypeError scenarios
assert.throws(function () { x[Symbol.toPrimitive].call("x") }, TypeError, "x[Symbol.toPrimitive].call('x'), toPrimitive throws TypeError for values that does not have SymbolData", "Symbol[Symbol.toPrimitive]: invalid argument");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive]() }, TypeError, "toPrimitive throws TypeError if no arguments are passed", "Symbol[Symbol.toPrimitive]: invalid argument");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call("") }, TypeError, "toPrimitive throws TypeError for values that does not have SymbolData", "Symbol[Symbol.toPrimitive]: invalid argument");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(null) }, TypeError, "toPrimitive throws TypeError for null", "Symbol[Symbol.toPrimitive]: invalid argument");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(undefined) }, TypeError, "toPrimitive throws TypeError for undefined", "Symbol[Symbol.toPrimitive]: invalid argument");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call({}) }, TypeError, "toPrimitive throws TypeError for object", "Symbol[Symbol.toPrimitive]: invalid argument");
assert.throws(function () { x[Symbol.toPrimitive].call("x") }, TypeError, "x[Symbol.toPrimitive].call('x'), toPrimitive throws TypeError for values that does not have SymbolData", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive]() }, TypeError, "toPrimitive throws TypeError if no arguments are passed", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(true) }, TypeError, "toPrimitive throws TypeError for boolean true", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(false) }, TypeError, "toPrimitive throws TypeError for boolean false", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(0) }, TypeError, "toPrimitive throws TypeError for number", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(NaN) }, TypeError, "toPrimitive throws TypeError for NaN", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call("") }, TypeError, "toPrimitive throws TypeError for values that does not have SymbolData", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call("abc") }, TypeError, "toPrimitive throws TypeError for values that does not have SymbolData", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(null) }, TypeError, "toPrimitive throws TypeError for null", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call(undefined) }, TypeError, "toPrimitive throws TypeError for undefined", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");
assert.throws(function () { Symbol.prototype[Symbol.toPrimitive].call({}) }, TypeError, "toPrimitive throws TypeError for object", "Symbol[Symbol.toPrimitive]: 'this' is not a Symbol object");

var z = Object(y);
assert.areEqual(y, Symbol.prototype[Symbol.toPrimitive].call(z), "y == Symbol.prototype[Symbol.toPrimitive].call(z)");
Expand Down
2 changes: 1 addition & 1 deletion test/es6/rlexe.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
<test>
<default>
<files>toPrimitive.js</files>
<compile-flags>-es6toprimitive -es6functionname -args summary -endargs</compile-flags>
<compile-flags>-es6regexsymbols -es6toprimitive -es6hasInstance -es6isconcatspreadable -es6tostringtag -es6functionname -args summary -endargs</compile-flags>
</default>
</test>
<test>
Expand Down
Loading

0 comments on commit f3c96ad

Please sign in to comment.