Make some builtin functions aware of abstract strings.

Summary:
Builtin functions that are side-effect free can trivially be modified to return abstract values if one or more of their arguments are abstract.

This pull request provides such behavior for the Error constructor, Error.prototype.toString and String.prototype.split and slice.
Closes https://github.com/facebook/prepack/pull/1110

Differential Revision: D6182526

Pulled By: hermanventer

fbshipit-source-id: d95c8045ff75c35f1b55dc668c080aa394ccb43d
This commit is contained in:
Herman Venter 2017-10-28 20:30:54 -07:00 committed by Facebook Github Bot
parent 0948e13141
commit 44aae027b7
6 changed files with 76 additions and 4 deletions

View File

@ -12,7 +12,13 @@
import type { Realm } from "../../realm.js";
import type { LexicalEnvironment } from "../../environment.js";
import { ObjectValue, FunctionValue, NativeFunctionValue, StringValue } from "../../values/index.js";
import { OrdinaryCreateFromConstructor, ToStringPartial, Get, DefinePropertyOrThrow } from "../../methods/index.js";
import {
DefinePropertyOrThrow,
Get,
OrdinaryCreateFromConstructor,
ToStringPartial,
ToStringValue,
} from "../../methods/index.js";
import invariant from "../../invariant.js";
import type { BabelNodeSourceLocation } from "babel-types";
@ -115,11 +121,11 @@ export function build(name: string, realm: Realm, inheritError?: boolean = true)
// 3. If message is not undefined, then
if (!message.mightBeUndefined()) {
// a. Let msg be ? ToString(message).
let msg = ToStringPartial(realm, message);
let msg = message.getType() === StringValue ? message : ToStringValue(realm, message);
// b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
let msgDesc = {
value: new StringValue(realm, msg),
value: msg,
writable: true,
enumerable: false,
configurable: true,

View File

@ -10,13 +10,17 @@
/* @flow */
import type { Realm } from "../../realm.js";
import { ObjectValue, StringValue, UndefinedValue } from "../../values/index.js";
import { AbstractValue, ObjectValue, StringValue, UndefinedValue } from "../../values/index.js";
import { ToStringPartial, Get } from "../../methods/index.js";
import buildExpressionTemplate from "../../utils/builder.js";
export default function(realm: Realm, obj: ObjectValue): void {
return build("Error", realm, obj);
}
const tsTemplateSrc = "(A).toString()";
const tsTemplate = buildExpressionTemplate(tsTemplateSrc);
export function build(name: string, realm: Realm, obj: ObjectValue): void {
// ECMA262 19.5.3.2
obj.defineNativeProperty("message", realm.intrinsics.emptyString);
@ -36,12 +40,18 @@ export function build(name: string, realm: Realm, obj: ObjectValue): void {
// 3. Let name be ? Get(O, "name").
let nameValue = Get(realm, O, "name");
if (nameValue instanceof AbstractValue) {
return AbstractValue.createFromTemplate(realm, tsTemplate, StringValue, [O], tsTemplateSrc);
}
// 4. If name is undefined, let name be "Error"; otherwise let name be ? ToString(name).
let nameString = nameValue instanceof UndefinedValue ? "Error" : ToStringPartial(realm, nameValue);
// 5. Let msg be ? Get(O, "message").
let msg = Get(realm, O, "message");
if (msg instanceof AbstractValue) {
return AbstractValue.createFromTemplate(realm, tsTemplate, StringValue, [O], tsTemplateSrc);
}
// 6. If msg is undefined, let msg be the empty String; otherwise let msg be ? ToString(msg).
msg = msg instanceof UndefinedValue ? "" : ToStringPartial(realm, msg);

View File

@ -29,6 +29,12 @@ import {
import { SplitMatch, RequireObjectCoercible } from "../../methods/abstract.js";
import { HasSomeCompatibleType } from "../../methods/has.js";
import invariant from "../../invariant.js";
import buildExpressionTemplate from "../../utils/builder.js";
const sliceTemplateSrc = "(A).slice(B,C)";
const sliceTemplate = buildExpressionTemplate(sliceTemplateSrc);
const splitTemplateSrc = "(A).split(B,C)";
const splitTemplate = buildExpressionTemplate(splitTemplateSrc);
export default function(realm: Realm, obj: ObjectValue): ObjectValue {
// ECMA262 21.1.3
@ -579,6 +585,10 @@ export default function(realm: Realm, obj: ObjectValue): ObjectValue {
// 1. Let O be ? RequireObjectCoercible(this value).
let O = RequireObjectCoercible(realm, context);
if (O instanceof AbstractValue && O.getType() === StringValue) {
return AbstractValue.createFromTemplate(realm, sliceTemplate, StringValue, [O, start, end], sliceTemplateSrc);
}
// 2. Let S be ? ToString(O).
let S = ToString(realm, O.throwIfNotConcrete());
@ -609,6 +619,16 @@ export default function(realm: Realm, obj: ObjectValue): ObjectValue {
// 1. Let O be ? RequireObjectCoercible(this value).
let O = RequireObjectCoercible(realm, context);
if (O instanceof AbstractValue && O.getType() === StringValue) {
return AbstractValue.createFromTemplate(
realm,
splitTemplate,
StringValue,
[O, separator, limit],
splitTemplateSrc
);
}
// 2. If separator is neither undefined nor null, then
if (!HasSomeCompatibleType(separator, UndefinedValue, NullValue)) {
// a. Let splitter be ? GetMethod(separator, @@split).

View File

@ -679,6 +679,31 @@ export function ToStringPartial(realm: Realm, val: string | Value): string {
return ToString(realm, typeof val === "string" ? val : val.throwIfNotConcrete());
}
export function ToStringValue(realm: Realm, val: Value): Value {
if (val.getType() === StringValue) return val;
let str;
if (typeof val === "string") {
str = val;
} else if (val instanceof NumberValue) {
str = val.value + "";
} else if (val instanceof UndefinedValue) {
str = "undefined";
} else if (val instanceof NullValue) {
str = "null";
} else if (val instanceof SymbolValue) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
} else if (val instanceof BooleanValue) {
str = val.value ? "true" : "false";
} else if (val instanceof ObjectValue) {
let primValue = ToPrimitiveOrAbstract(realm, val, "string");
if (primValue.getType() === StringValue) return primValue;
str = ToStringPartial(realm, primValue);
} else {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "unknown value type, can't coerce to string");
}
return new StringValue(realm, str);
}
// ECMA262 7.1.2
export function ToBoolean(realm: Realm, val: ConcreteValue): boolean {
if (val instanceof BooleanValue) {

View File

@ -0,0 +1,6 @@
let x = global.__abstract ? __abstract("string", "('abc')") : 'abc';
let err1 = new Error(x);
err1.name = x;
let err2 = new Error(err1);
inspect = function() { return "" + err2; }

View File

@ -0,0 +1,5 @@
let x = global.__abstract ? __abstract("string", "('a,b,c,d,e')") : 'a,b,c,d,e';
let sliced = x.slice(2, 3);
let split = x.split(',', 2);
inspect = function() { return sliced + split.join(':'); }