Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
428 lines
12 KiB
C++
428 lines
12 KiB
C++
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "VMPI.h"
|
|
|
|
#include <Mmsystem.h>
|
|
#include <time.h>
|
|
|
|
/*
|
|
* Windows implementation of platform-dependent date and time code
|
|
*/
|
|
static const double kMsecPerDay = 86400000;
|
|
static const double kMsecPerHour = 3600000;
|
|
static const double kMsecPerSecond = 1000;
|
|
static const double kMsecPerMinute = 60000;
|
|
|
|
static TIME_ZONE_INFORMATION UpdateTimeZoneInfo()
|
|
{
|
|
// protects gTimeZoneInfo and gGmtCache
|
|
// We're relying on the fact that static initialization is OK for spin locks.
|
|
static vmpi_spin_lock_t gTimeZoneInfoLock;
|
|
static TIME_ZONE_INFORMATION gTimeZoneInfo;
|
|
static SYSTEMTIME gGmtCache;
|
|
|
|
SYSTEMTIME gmt;
|
|
GetSystemTime(&gmt);
|
|
VMPI_lockAcquire(&gTimeZoneInfoLock);
|
|
if ((gmt.wMinute != gGmtCache.wMinute) ||
|
|
(gmt.wHour != gGmtCache.wHour) ||
|
|
(gmt.wDay != gGmtCache.wDay) ||
|
|
(gmt.wMonth != gGmtCache.wMonth) ||
|
|
(gmt.wYear != gGmtCache.wYear)
|
|
)
|
|
{
|
|
// Cache is invalid.
|
|
GetTimeZoneInformation(&gTimeZoneInfo);
|
|
gGmtCache = gmt;
|
|
}
|
|
TIME_ZONE_INFORMATION tz = gTimeZoneInfo;
|
|
VMPI_lockRelease(&gTimeZoneInfoLock);
|
|
return tz;
|
|
}
|
|
|
|
double VMPI_getLocalTimeOffset()
|
|
{
|
|
TIME_ZONE_INFORMATION tz = UpdateTimeZoneInfo();
|
|
return -tz.Bias * 60.0 * 1000.0;
|
|
}
|
|
|
|
// Bugzilla 668442, fix this evil of working around tangle of
|
|
// dependencies with local declarations (below are from Date.cpp)
|
|
namespace avmplus
|
|
{
|
|
int WeekDay(double t);
|
|
double MakeDate(double day, double time);
|
|
double MakeDay(double year, double month, double date);
|
|
double MakeTime(double hour, double min, double sec, double ms);
|
|
int YearFromTime(double t);
|
|
}
|
|
|
|
static double ConvertWin32DST(int year, SYSTEMTIME *st)
|
|
{
|
|
// The StandardDate and DaylightDate members of
|
|
// TIMEZONE_INFORMATION may be specified in two ways:
|
|
// 1. An absolute time and date
|
|
// 2. A month, day of week and week in month, and a time of day
|
|
|
|
if (st->wYear != 0) {
|
|
return avmplus::MakeDate(avmplus::MakeDay(year,
|
|
st->wMonth - 1,
|
|
st->wDay),
|
|
avmplus::MakeTime(st->wHour,
|
|
st->wMinute,
|
|
st->wSecond,
|
|
st->wMilliseconds));
|
|
}
|
|
|
|
double timeValue = avmplus::MakeDate(avmplus::MakeDay(year,
|
|
st->wMonth-1,
|
|
1),
|
|
avmplus::MakeTime(st->wHour,
|
|
st->wMinute,
|
|
st->wSecond,
|
|
st->wMilliseconds));
|
|
|
|
double nextMonth;
|
|
if (st->wMonth < 12) {
|
|
nextMonth = avmplus::MakeDate(avmplus::MakeDay(year, st->wMonth, 1), 0);
|
|
} else {
|
|
nextMonth = avmplus::MakeDate(avmplus::MakeDay(year + 1, 0, 1), 0);
|
|
}
|
|
|
|
int dayOfWeek = avmplus::WeekDay(timeValue);
|
|
if (dayOfWeek != st->wDayOfWeek) {
|
|
int dayDelta = st->wDayOfWeek - dayOfWeek;
|
|
if (dayDelta < 0) {
|
|
dayDelta += 7;
|
|
}
|
|
timeValue += (double)kMsecPerDay * dayDelta;
|
|
}
|
|
|
|
// Advance appropriate # of weeks
|
|
timeValue += (double)kMsecPerDay * 7.0 * (st->wDay - 1);
|
|
|
|
// Cap it at the end of the month
|
|
while (timeValue >= nextMonth) {
|
|
timeValue -= (double)kMsecPerDay * 7.0;
|
|
}
|
|
|
|
return timeValue;
|
|
}
|
|
|
|
//time is passed in as milliseconds from UTC.
|
|
double VMPI_getDaylightSavingsTA(double time)
|
|
{
|
|
// On Windows, ask the OS what the daylight saving time bias
|
|
// is. If it's zero, perform no adjustment.
|
|
TIME_ZONE_INFORMATION tz = UpdateTimeZoneInfo();
|
|
if (tz.DaylightBias != -60 || tz.DaylightDate.wMonth == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// In most of the US, Daylight Saving Time begins on the
|
|
// first Sunday of April at 2 AM. It ends at 2 am on
|
|
// the last Sunday of October.
|
|
|
|
// 1. Compute year this time represents.
|
|
int year = avmplus::YearFromTime(time);
|
|
|
|
// 2. Compute time that daylight saving time begins
|
|
double timeD = ConvertWin32DST(year, &tz.DaylightDate);
|
|
|
|
// 3. Compute time that standard time begins
|
|
double timeS = ConvertWin32DST(year, &tz.StandardDate);
|
|
|
|
// Subtract the daylight bias from the standard transition time
|
|
timeS -= kMsecPerHour;
|
|
|
|
// The times we have calculated are in local time,
|
|
// but "time" was passed in as UTC. Convert it to local time.
|
|
time += VMPI_getLocalTimeOffset();
|
|
|
|
// Does time fall in the daylight saving period?
|
|
|
|
// Where Daylight savings time is earlier in the year than standard time
|
|
if(timeS > timeD)
|
|
{
|
|
if (time >= timeD && time < timeS)
|
|
return kMsecPerHour;
|
|
else
|
|
return 0;
|
|
}
|
|
// Where Daylight savings time is later in the year than standard time
|
|
else
|
|
{
|
|
if (time >= timeS && time < timeD)
|
|
return 0;
|
|
else
|
|
return kMsecPerHour;
|
|
}
|
|
|
|
}
|
|
|
|
#define FILETIME_EPOCH_BIAS ((LONGLONG)116444736000000000)
|
|
#define FILETIME_MS_FACTOR (10000.0)
|
|
|
|
static double NormalizeFileTime(FILETIME* ft)
|
|
{
|
|
LARGE_INTEGER li;
|
|
li.LowPart = ft->dwLowDateTime;
|
|
li.HighPart = ft->dwHighDateTime;
|
|
|
|
return ((double) (li.QuadPart - FILETIME_EPOCH_BIAS)) / FILETIME_MS_FACTOR;
|
|
}
|
|
|
|
static double NormalizeSystemTime(SYSTEMTIME* st)
|
|
{
|
|
FILETIME ft;
|
|
SystemTimeToFileTime(st, &ft);
|
|
return NormalizeFileTime(&ft);
|
|
}
|
|
|
|
double VMPI_getDate()
|
|
{
|
|
SYSTEMTIME stime;
|
|
GetSystemTime(&stime);
|
|
return NormalizeSystemTime(&stime);
|
|
}
|
|
|
|
uint64_t VMPI_getTime()
|
|
{
|
|
#if AVMSYSTEM_WINDOWSSTOREAPP
|
|
LARGE_INTEGER counter;
|
|
LARGE_INTEGER frequency;
|
|
QueryPerformanceCounter(&counter);
|
|
QueryPerformanceFrequency(&frequency);
|
|
return (counter.QuadPart * 1000) / frequency.QuadPart;
|
|
#else
|
|
return timeGetTime();
|
|
#endif // AVMSYSTEM_WINDOWSSTOREAPP
|
|
}
|
|
|
|
// On Windows, _vsnprintf isn't reliable (no NUL termination) and _vsnprintf_s
|
|
// is not compatible (throws an exception if the format is wrong). Easy
|
|
// enough to create our own on top of _vsnprintf by adding the terminator
|
|
// and adjusting the return value.
|
|
//
|
|
// ANSI C requires vsnprintf to return the number of characters that would have
|
|
// been printed had the buffer been unrestricted. The MSVC _vsnprintf does not
|
|
// do that. Instead, return the number of characters written not including
|
|
// the NUL in all cases.
|
|
|
|
int VMPI_vsnprintf(char *s, size_t n, const char *format, va_list args)
|
|
{
|
|
int ret = _vsnprintf(s, n, format, args);
|
|
if (ret == -1)
|
|
ret = int(n);
|
|
if (ret == int(n)) {
|
|
s[n-1] = 0;
|
|
ret--;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// On Windows, _snprintf isn't reliable (no NUL termination) and _snpritnf_s
|
|
// is not compatible (throws an exception if the format is wrong). Easy
|
|
// enough to create our own on top of VMPI_vsnprintf.
|
|
//
|
|
// Return what VMPI_vsnprintf returns; see notes above.
|
|
|
|
int VMPI_snprintf(char *s, size_t n, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
int ret = VMPI_vsnprintf(s, n, format, args);
|
|
va_end(format);
|
|
return ret;
|
|
}
|
|
|
|
void* VMPI_alloc(size_t size)
|
|
{
|
|
return HeapAlloc(GetProcessHeap(), 0, size);
|
|
}
|
|
|
|
void VMPI_free(void* ptr)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, ptr);
|
|
}
|
|
|
|
size_t VMPI_size(void* ptr)
|
|
{
|
|
return HeapSize(GetProcessHeap(), 0, ptr);
|
|
}
|
|
|
|
typedef void (*LoggingFunction)(const char*);
|
|
extern LoggingFunction GetCurrentLogFunction();
|
|
|
|
void VMPI_log(const char* message)
|
|
{
|
|
#ifndef UNDER_CE
|
|
::OutputDebugStringA(message);
|
|
#endif
|
|
LoggingFunction logFunc = GetCurrentLogFunction();
|
|
if(logFunc)
|
|
logFunc(message);
|
|
else {
|
|
printf("%s",message);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
const char *VMPI_getenv(const char *env)
|
|
{
|
|
const char *val = NULL;
|
|
(void)env;
|
|
// Environment variables are not available for Windows Store applications.
|
|
#ifndef AVMSYSTEM_WINDOWSSTOREAPP
|
|
#ifndef UNDER_CE
|
|
val = getenv(env);
|
|
#endif
|
|
#endif // AVMSYSTEM_WINDOWSSTOREAPP
|
|
return val;
|
|
}
|
|
|
|
// Call VMPI_getPerformanceFrequency() once to initialize its cache; avoids thread safety issues.
|
|
static uint64_t unused_value = VMPI_getPerformanceFrequency();
|
|
|
|
uint64_t VMPI_getPerformanceFrequency()
|
|
{
|
|
// *** NOTE ABOUT THREAD SAFETY ***
|
|
//
|
|
// This static ought to be safe because it is initialized by a call at startup
|
|
// (see lines above this function), before any AvmCores are created.
|
|
|
|
static uint64_t gPerformanceFrequency = 0;
|
|
if (gPerformanceFrequency == 0) {
|
|
QueryPerformanceFrequency((LARGE_INTEGER*)&gPerformanceFrequency);
|
|
}
|
|
return gPerformanceFrequency;
|
|
}
|
|
|
|
uint64_t VMPI_getPerformanceCounter()
|
|
{
|
|
LARGE_INTEGER value;
|
|
QueryPerformanceCounter(&value);
|
|
return value.QuadPart;
|
|
}
|
|
|
|
|
|
// Defined in GenericDebugUtils.cpp to prevent them from being inlined below
|
|
|
|
extern void CallWithRegistersSaved2(void (*fn)(void* stackPointer, void* arg), void* arg, void* buf);
|
|
extern void CallWithRegistersSaved3(void (*fn)(void* stackPointer, void* arg), void* arg, void* buf);
|
|
|
|
void VMPI_callWithRegistersSaved(void (*fn)(void* stackPointer, void* arg), void* arg)
|
|
{
|
|
jmp_buf buf;
|
|
VMPI_setjmpNoUnwind(buf); // Save registers
|
|
CallWithRegistersSaved2(fn, arg, &buf); // Computes the stack pointer, calls fn
|
|
CallWithRegistersSaved3(fn, &arg, &buf); // Probably prevents the previous call from being a tail call
|
|
}
|
|
|
|
static size_t computePagesize()
|
|
{
|
|
SYSTEM_INFO sysinfo;
|
|
|
|
#if AVMSYSTEM_WINDOWSSTOREAPP
|
|
GetNativeSystemInfo(&sysinfo);
|
|
#else
|
|
GetSystemInfo(&sysinfo);
|
|
#endif // AVMSYSTEM_WINDOWSSTOREAPP
|
|
|
|
return sysinfo.dwPageSize;
|
|
}
|
|
|
|
// Private to AAVMPI_getVMPageSize (but initialized here to avoid threading concerns).
|
|
// DO NOT REFERENCE THIS VARIABLE ELSEWHERE. Always call AAVMPI_getVMPageSize.
|
|
|
|
static size_t pagesize = computePagesize();
|
|
|
|
size_t VMPI_getVMPageSize()
|
|
{
|
|
return pagesize;
|
|
}
|
|
|
|
#if AVMSYSTEM_WINDOWSSTOREAPP
|
|
|
|
using namespace Windows::System::Threading;
|
|
using namespace Windows::Foundation;
|
|
|
|
struct VMPI_WinTimerData : VMPI_TimerData
|
|
{
|
|
ThreadPoolTimer^ timer;
|
|
};
|
|
|
|
uintptr_t VMPI_startTimer(uint32_t micros, VMPI_TimerClient *client)
|
|
{
|
|
VMPI_WinTimerData *data = (VMPI_WinTimerData *) VMPI_alloc(sizeof(VMPI_WinTimerData));
|
|
data->init(micros, client);
|
|
data->timer = nullptr;
|
|
|
|
TimeSpan timeSpan;
|
|
timeSpan.Duration = micros / 1000;
|
|
|
|
data->timer = ThreadPoolTimer::CreatePeriodicTimer(ref new TimerElapsedHandler([&] (ThreadPoolTimer^ timer) {
|
|
data->client->tick();
|
|
}
|
|
), timeSpan);
|
|
return (uintptr_t)data;
|
|
}
|
|
|
|
void VMPI_stopTimer(uintptr_t ptr)
|
|
{
|
|
VMPI_WinTimerData *data = (VMPI_WinTimerData *)ptr;
|
|
data->timer->Cancel();
|
|
data->timer = nullptr;
|
|
VMPI_free(data);
|
|
}
|
|
|
|
#else
|
|
|
|
// Timer data, Windows specific data
|
|
struct VMPI_WinTimerData : VMPI_TimerData
|
|
{
|
|
// Platform-specific data
|
|
unsigned int timerId;
|
|
};
|
|
|
|
// The timer callback proc, called when the timer fires
|
|
void CALLBACK timerProc(UINT, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR)
|
|
{
|
|
VMPI_TimerData *data = (VMPI_TimerData*)dwUser;
|
|
data->client->tick();
|
|
}
|
|
|
|
// The constant TIME_KILL_SYNCHRONOUS is only available if WINVER >= 0x0501 (== WinXP)
|
|
static const UINT kTimeKillSynchronous = 0x0100;
|
|
|
|
// Starts a timer
|
|
uintptr_t VMPI_startTimer(uint32_t micros, VMPI_TimerClient *client)
|
|
{
|
|
VMPI_WinTimerData *data = (VMPI_WinTimerData *) VMPI_alloc(sizeof(VMPI_WinTimerData));
|
|
data->init(micros, client);
|
|
|
|
data->timerId = timeSetEvent(micros / 1000, micros / 1000, (LPTIMECALLBACK)timerProc, (DWORD_PTR)data,
|
|
TIME_PERIODIC | TIME_CALLBACK_FUNCTION
|
|
#ifndef UNDER_CE
|
|
| kTimeKillSynchronous
|
|
#endif
|
|
);
|
|
|
|
return (uintptr_t)data;
|
|
}
|
|
|
|
// Stops a timer
|
|
void VMPI_stopTimer(uintptr_t data)
|
|
{
|
|
UINT timerId = ((VMPI_WinTimerData *)data)->timerId;
|
|
if (timerId)
|
|
timeKillEvent((UINT)timerId);
|
|
VMPI_free((VMPI_WinTimerData *)data);
|
|
}
|
|
|
|
#endif // AVMSYSTEM_WINDOWSSTOREAPP
|