@ -35,14 +35,26 @@
// use external string resources.
// use external string resources.
# define EXTERN_APEX 0xFBEE9
# define EXTERN_APEX 0xFBEE9
// TODO(addaleax): These should all have better error messages. In particular,
// they should mention what the actual limits are.
# define SB_MALLOC_FAILED_ERROR \
v8 : : Exception : : Error ( OneByteString ( isolate , " \" toString() \" failed " ) )
# define SB_STRING_TOO_LONG_ERROR \
v8 : : Exception : : Error ( OneByteString ( isolate , " \" toString() \" failed " ) )
# define SB_BUFFER_CREATION_ERROR \
v8 : : Exception : : Error ( OneByteString ( isolate , " \" toString() \" failed " ) )
# define SB_BUFFER_SIZE_EXCEEDED_ERROR \
v8 : : Exception : : Error ( OneByteString ( isolate , " \" toString() \" failed " ) )
namespace node {
namespace node {
using v8 : : EscapableHandleScope ;
using v8 : : HandleScope ;
using v8 : : HandleScope ;
using v8 : : Isolate ;
using v8 : : Isolate ;
using v8 : : Local ;
using v8 : : Local ;
using v8 : : MaybeLocal ;
using v8 : : MaybeLocal ;
using v8 : : Object ;
using v8 : : String ;
using v8 : : String ;
using v8 : : Value ;
using v8 : : Value ;
@ -68,46 +80,56 @@ class ExternString: public ResourceType {
return length ( ) * sizeof ( * data ( ) ) ;
return length ( ) * sizeof ( * data ( ) ) ;
}
}
static Local < String > NewFromCopy ( Isolate * isolate ,
static MaybeLocal < Value > NewFromCopy ( Isolate * isolate ,
const TypeName * data ,
const TypeName * data ,
size_t length ) {
size_t length ,
EscapableHandleScope scope ( isolate ) ;
Local < Value > * error ) {
if ( length = = 0 )
if ( length = = 0 )
return scope . Escape ( String : : Empty ( isolate ) ) ;
return String : : Empty ( isolate ) ;
if ( length < EXTERN_APEX )
return NewSimpleFromCopy ( isolate , data , length , error ) ;
TypeName * new_data = node : : UncheckedMalloc < TypeName > ( length ) ;
TypeName * new_data = node : : UncheckedMalloc < TypeName > ( length ) ;
if ( new_data = = nullptr ) {
if ( new_data = = nullptr ) {
return Local < String > ( ) ;
* error = SB_MALLOC_FAILED_ERROR ;
return MaybeLocal < Value > ( ) ;
}
}
memcpy ( new_data , data , length * sizeof ( * new_data ) ) ;
memcpy ( new_data , data , length * sizeof ( * new_data ) ) ;
return scope . Escape ( ExternString < ResourceType , TypeName > : : New ( isolate ,
return ExternString < ResourceType , TypeName > : : New ( isolate ,
new_data ,
new_data ,
length ) ) ;
length ,
error ) ;
}
}
// uses "data" for external resource, and will be free'd on gc
// uses "data" for external resource, and will be free'd on gc
static Local < String > New ( Isolate * isolate ,
static MaybeLocal < Value > New ( Isolate * isolate ,
const TypeName * data ,
TypeName * data ,
size_t length ) {
size_t length ,
EscapableHandleScope scope ( isolate ) ;
Local < Value > * error ) {
if ( length = = 0 )
if ( length = = 0 )
return scope . Escape ( String : : Empty ( isolate ) ) ;
return String : : Empty ( isolate ) ;
if ( length < EXTERN_APEX ) {
MaybeLocal < Value > str = NewSimpleFromCopy ( isolate , data , length , error ) ;
free ( data ) ;
return str ;
}
ExternString * h_str = new ExternString < ResourceType , TypeName > ( isolate ,
ExternString * h_str = new ExternString < ResourceType , TypeName > ( isolate ,
data ,
data ,
length ) ;
length ) ;
MaybeLocal < String > str = NewExternal ( isolate , h_str ) ;
MaybeLocal < Value > str = NewExternal ( isolate , h_str ) ;
isolate - > AdjustAmountOfExternalAllocatedMemory ( h_str - > byte_length ( ) ) ;
isolate - > AdjustAmountOfExternalAllocatedMemory ( h_str - > byte_length ( ) ) ;
if ( str . IsEmpty ( ) ) {
if ( str . IsEmpty ( ) ) {
delete h_str ;
delete h_str ;
return Local < String > ( ) ;
* error = SB_STRING_TOO_LONG_ERROR ;
return MaybeLocal < Value > ( ) ;
}
}
return scope . Escape ( s tr . ToLocalChecked ( ) ) ;
return str . ToLocalChecked ( ) ;
}
}
inline Isolate * isolate ( ) const { return isolate_ ; }
inline Isolate * isolate ( ) const { return isolate_ ; }
@ -115,8 +137,14 @@ class ExternString: public ResourceType {
private :
private :
ExternString ( Isolate * isolate , const TypeName * data , size_t length )
ExternString ( Isolate * isolate , const TypeName * data , size_t length )
: isolate_ ( isolate ) , data_ ( data ) , length_ ( length ) { }
: isolate_ ( isolate ) , data_ ( data ) , length_ ( length ) { }
static MaybeLocal < String > NewExternal ( Isolate * isolate ,
static MaybeLocal < Value > NewExternal ( Isolate * isolate ,
ExternString * h_str ) ;
ExternString * h_str ) ;
// This method does not actually create ExternString instances.
static MaybeLocal < Value > NewSimpleFromCopy ( Isolate * isolate ,
const TypeName * data ,
size_t length ,
Local < Value > * error ) ;
Isolate * isolate_ ;
Isolate * isolate_ ;
const TypeName * data_ ;
const TypeName * data_ ;
@ -131,16 +159,51 @@ typedef ExternString<String::ExternalStringResource,
template < >
template < >
MaybeLocal < String > ExternOneByteString : : NewExternal (
MaybeLocal < Value > ExternOneByteString : : NewExternal (
Isolate * isolate , ExternOneByteString * h_str ) {
Isolate * isolate , ExternOneByteString * h_str ) {
return String : : NewExternalOneByte ( isolate , h_str ) ;
return String : : NewExternalOneByte ( isolate , h_str ) . FromMaybe ( Local < Value > ( ) ) ;
}
}
template < >
template < >
MaybeLocal < String > ExternTwoByteString : : NewExternal (
MaybeLocal < Value > ExternTwoByteString : : NewExternal (
Isolate * isolate , ExternTwoByteString * h_str ) {
Isolate * isolate , ExternTwoByteString * h_str ) {
return String : : NewExternalTwoByte ( isolate , h_str ) ;
return String : : NewExternalTwoByte ( isolate , h_str ) . FromMaybe ( Local < Value > ( ) ) ;
}
template < >
MaybeLocal < Value > ExternOneByteString : : NewSimpleFromCopy ( Isolate * isolate ,
const char * data ,
size_t length ,
Local < Value > * error ) {
MaybeLocal < String > str =
String : : NewFromOneByte ( isolate ,
reinterpret_cast < const uint8_t * > ( data ) ,
v8 : : NewStringType : : kNormal ,
length ) ;
if ( str . IsEmpty ( ) ) {
* error = SB_STRING_TOO_LONG_ERROR ;
return MaybeLocal < Value > ( ) ;
}
return str . ToLocalChecked ( ) ;
}
template < >
MaybeLocal < Value > ExternTwoByteString : : NewSimpleFromCopy ( Isolate * isolate ,
const uint16_t * data ,
size_t length ,
Local < Value > * error ) {
MaybeLocal < String > str =
String : : NewFromTwoByte ( isolate ,
data ,
v8 : : NewStringType : : kNormal ,
length ) ;
if ( str . IsEmpty ( ) ) {
* error = SB_STRING_TOO_LONG_ERROR ;
return MaybeLocal < Value > ( ) ;
}
return str . ToLocalChecked ( ) ;
}
}
} // anonymous namespace
} // anonymous namespace
@ -610,97 +673,93 @@ static size_t hex_encode(const char* src, size_t slen, char* dst, size_t dlen) {
}
}
# define CHECK_BUFLEN_IN_RANGE(len) \
do { \
if ( ( len ) > Buffer : : kMaxLength ) { \
* error = SB_BUFFER_SIZE_EXCEEDED_ERROR ; \
return MaybeLocal < Value > ( ) ; \
} \
} while ( 0 )
Local < Value > StringBytes : : Encode ( Isolate * isolate ,
const char * buf ,
size_t buflen ,
enum encoding encoding ) {
EscapableHandleScope scope ( isolate ) ;
MaybeLocal < Value > StringBytes : : Encode ( Isolate * isolate ,
const char * buf ,
size_t buflen ,
enum encoding encoding ,
Local < Value > * error ) {
CHECK_NE ( encoding , UCS2 ) ;
CHECK_NE ( encoding , UCS2 ) ;
CHECK_LE ( buflen , Buffer : : kMaxLength ) ;
CHECK_BUFLEN_IN_RANGE ( buflen ) ;
if ( ! buflen & & encoding ! = BUFFER )
return scope . Escape ( String : : Empty ( isolate ) ) ;
* error = Local < Value > ( ) ;
if ( ! buflen & & encoding ! = BUFFER ) {
return String : : Empty ( isolate ) ;
}
MaybeLocal < String > val ;
Local < String > val ;
switch ( encoding ) {
switch ( encoding ) {
case BUFFER :
case BUFFER :
{
{
Local < Object > vbuf =
auto maybe_buf = Buffer : : Copy ( isolate , buf , buflen ) ;
Buffer : : Copy ( isolate , buf , buflen ) . ToLocalChecked ( ) ;
if ( maybe_buf . IsEmpty ( ) ) {
return scope . Escape ( vbuf ) ;
* error = SB_BUFFER_CREATION_ERROR ;
return MaybeLocal < Value > ( ) ;
}
return maybe_buf . ToLocalChecked ( ) ;
}
}
case ASCII :
case ASCII :
if ( contains_non_ascii ( buf , buflen ) ) {
if ( contains_non_ascii ( buf , buflen ) ) {
char * out = node : : UncheckedMalloc ( buflen ) ;
char * out = node : : UncheckedMalloc ( buflen ) ;
if ( out = = nullptr ) {
if ( out = = nullptr ) {
return Local < String > ( ) ;
* error = SB_MALLOC_FAILED_ERROR ;
return MaybeLocal < Value > ( ) ;
}
}
force_ascii ( buf , out , buflen ) ;
force_ascii ( buf , out , buflen ) ;
if ( buflen < EXTERN_APEX ) {
return ExternOneByteString : : New ( isolate , out , buflen , error ) ;
val = OneByteString ( isolate , out , buflen ) ;
free ( out ) ;
} else {
val = ExternOneByteString : : New ( isolate , out , buflen ) ;
}
} else {
} else {
if ( buflen < EXTERN_APEX )
return ExternOneByteString : : NewFromCopy ( isolate , buf , buflen , error ) ;
val = OneByteString ( isolate , buf , buflen ) ;
else
val = ExternOneByteString : : NewFromCopy ( isolate , buf , buflen ) ;
}
}
break ;
case UTF8 :
case UTF8 :
val = String : : NewFromUtf8 ( isolate ,
val = String : : NewFromUtf8 ( isolate ,
buf ,
buf ,
String : : kNormalString ,
v8 : : New StringType : : kNormal ,
buflen ) ;
buflen ) ;
break ;
if ( val . IsEmpty ( ) ) {
* error = SB_STRING_TOO_LONG_ERROR ;
return MaybeLocal < Value > ( ) ;
}
return val . ToLocalChecked ( ) ;
case LATIN1 :
case LATIN1 :
if ( buflen < EXTERN_APEX )
return ExternOneByteString : : NewFromCopy ( isolate , buf , buflen , error ) ;
val = OneByteString ( isolate , buf , buflen ) ;
else
val = ExternOneByteString : : NewFromCopy ( isolate , buf , buflen ) ;
break ;
case BASE64 : {
case BASE64 : {
size_t dlen = base64_encoded_size ( buflen ) ;
size_t dlen = base64_encoded_size ( buflen ) ;
char * dst = node : : UncheckedMalloc ( dlen ) ;
char * dst = node : : UncheckedMalloc ( dlen ) ;
if ( dst = = nullptr ) {
if ( dst = = nullptr ) {
return Local < String > ( ) ;
* error = SB_MALLOC_FAILED_ERROR ;
return MaybeLocal < Value > ( ) ;
}
}
size_t written = base64_encode ( buf , buflen , dst , dlen ) ;
size_t written = base64_encode ( buf , buflen , dst , dlen ) ;
CHECK_EQ ( written , dlen ) ;
CHECK_EQ ( written , dlen ) ;
if ( dlen < EXTERN_APEX ) {
return ExternOneByteString : : New ( isolate , dst , dlen , error ) ;
val = OneByteString ( isolate , dst , dlen ) ;
free ( dst ) ;
} else {
val = ExternOneByteString : : New ( isolate , dst , dlen ) ;
}
break ;
}
}
case HEX : {
case HEX : {
size_t dlen = buflen * 2 ;
size_t dlen = buflen * 2 ;
char * dst = node : : UncheckedMalloc ( dlen ) ;
char * dst = node : : UncheckedMalloc ( dlen ) ;
if ( dst = = nullptr ) {
if ( dst = = nullptr ) {
return Local < String > ( ) ;
* error = SB_MALLOC_FAILED_ERROR ;
return MaybeLocal < Value > ( ) ;
}
}
size_t written = hex_encode ( buf , buflen , dst , dlen ) ;
size_t written = hex_encode ( buf , buflen , dst , dlen ) ;
CHECK_EQ ( written , dlen ) ;
CHECK_EQ ( written , dlen ) ;
if ( dlen < EXTERN_APEX ) {
return ExternOneByteString : : New ( isolate , dst , dlen , error ) ;
val = OneByteString ( isolate , dst , dlen ) ;
free ( dst ) ;
} else {
val = ExternOneByteString : : New ( isolate , dst , dlen ) ;
}
break ;
}
}
default :
default :
@ -708,13 +767,17 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
break ;
break ;
}
}
return scope . Escape ( val ) ;
UNREACHABLE ( ) ;
}
}
Local < Value > StringBytes : : Encode ( Isolate * isolate ,
MaybeLocal < Value > StringBytes : : Encode ( Isolate * isolate ,
const uint16_t * buf ,
const uint16_t * buf ,
size_t buflen ) {
size_t buflen ,
Local < Value > * error ) {
CHECK_BUFLEN_IN_RANGE ( buflen ) ;
* error = Local < Value > ( ) ;
// Node's "ucs2" encoding expects LE character data inside a
// Node's "ucs2" encoding expects LE character data inside a
// Buffer, so we need to reorder on BE platforms. See
// Buffer, so we need to reorder on BE platforms. See
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
@ -727,24 +790,15 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
buf = & dst [ 0 ] ;
buf = & dst [ 0 ] ;
}
}
Local < String > val ;
return ExternTwoByteString : : NewFromCopy ( isolate , buf , buflen , error ) ;
if ( buflen < EXTERN_APEX ) {
val = String : : NewFromTwoByte ( isolate ,
buf ,
String : : kNormalString ,
buflen ) ;
} else {
val = ExternTwoByteString : : NewFromCopy ( isolate , buf , buflen ) ;
}
return val ;
}
}
Local < Value > StringBytes : : Encode ( Isolate * isolate ,
MaybeLocal < Value > StringBytes : : Encode ( Isolate * isolate ,
const char * buf ,
const char * buf ,
enum encoding encoding ) {
enum encoding encoding ,
Local < Value > * error ) {
const size_t len = strlen ( buf ) ;
const size_t len = strlen ( buf ) ;
Local < Value > ret ;
MaybeLocal < Value > ret ;
if ( encoding = = UCS2 ) {
if ( encoding = = UCS2 ) {
// In Node, UCS2 means utf16le. The data must be in little-endian
// In Node, UCS2 means utf16le. The data must be in little-endian
// order and must be aligned on 2-bytes. This returns an empty
// order and must be aligned on 2-bytes. This returns an empty
@ -763,9 +817,9 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
}
}
ret = vec . empty ( ) ?
ret = vec . empty ( ) ?
static_cast < Local < Value > > ( String : : Empty ( isolate ) )
static_cast < Local < Value > > ( String : : Empty ( isolate ) )
: StringBytes : : Encode ( isolate , & vec [ 0 ] , vec . size ( ) ) ;
: StringBytes : : Encode ( isolate , & vec [ 0 ] , vec . size ( ) , error ) ;
} else {
} else {
ret = StringBytes : : Encode ( isolate , buf , len , encoding ) ;
ret = StringBytes : : Encode ( isolate , buf , len , encoding , error ) ;
}
}
return ret ;
return ret ;
}
}